Merge branch 'nightly'
Conflicts: INSTALL
34
EVENTS.txt
@ -2,6 +2,13 @@ InitializePlugin: a chance to initialize a plugin in a complete environment
|
||||
|
||||
CleanupPlugin: a chance to cleanup a plugin at the end of a program
|
||||
|
||||
StartActionExecute: Right before the "prepare" call of the current Action
|
||||
- $action: the current Action object
|
||||
- &$args: array of arguments, referenced so you can modify the array
|
||||
|
||||
EndActionExecute: Right after the "handle" call of the current Action
|
||||
- $action: the current Action object
|
||||
|
||||
StartPrimaryNav: Showing the primary nav menu
|
||||
- $action: the current action
|
||||
|
||||
@ -1444,6 +1451,9 @@ StartResizeImageFile: Hook to resize an image and output it to a file. No matchi
|
||||
- $outpath: string with output filepath
|
||||
- $box: array with size ('width', 'height') and boundary box('x', 'y', 'w', 'h').
|
||||
|
||||
FillImageFileMetadata: Get more metadata about the ImageFile if it is perhaps not a real local file
|
||||
- $imagefile ImageFile object which we're getting metadata for (such as animated status, width/height etc.)
|
||||
|
||||
StartShowAttachmentRepresentation: Attachment representation, full file (or in rare cases thumbnails/previews).
|
||||
- $out: HTMLOutputter class to use for outputting HTML.
|
||||
- $file: 'File' object which we're going to show representation for.
|
||||
@ -1463,3 +1473,27 @@ StartNotifyMentioned: During notice distribution, we send notifications (email,
|
||||
EndNotifyMentioned: During notice distribution, we send notifications (email, im...) to the profiles who were somehow mentioned.
|
||||
- $stored: Notice object that is being distributed.
|
||||
- $mentioned_ids: Array of profile IDs (not just for local users) who got mentioned by the notice.
|
||||
|
||||
StartHomeStubNavItems: Go back Home nav items. Default includes just one item 'home'
|
||||
- $out: HTMLOutputter used to output (usually an Action, but not always!)
|
||||
- &$items: Referenced array of items in the nav (add if desired)
|
||||
|
||||
EndHomeStubNavItems:
|
||||
- $out: HTMLOutputter used to output (usually an Action, but not always!)
|
||||
- $items: array of menu items
|
||||
|
||||
StartSubMenu: Before outputting a submenu (including enclosing tags) to HTML
|
||||
- $out: HTMLOutputter used to output (usually an Action, but not always!)
|
||||
- $menu: The Menu object outputted as a submenu.
|
||||
- $label: Localized text which represents the menu item.
|
||||
|
||||
EndSubMenu: After outputting a submenu (including enclosing tags) to HTML
|
||||
- $out: HTMLOutputter used to output (usually an Action, but not always!)
|
||||
- $menu: The Menu object outputted as a submenu.
|
||||
- $label: Localized text which represents the menu item.
|
||||
|
||||
StartDocNav: Before outputting the docs Nav
|
||||
- $nav: The DoclNav widget
|
||||
|
||||
EndDocNav: After outputting the docs Nav
|
||||
- $nav: The DoclNav widget
|
||||
|
238
INSTALL
@ -6,13 +6,16 @@ TABLE OF CONTENTS
|
||||
* Installation
|
||||
- Getting it up and running
|
||||
- Fancy URLs
|
||||
- Themes
|
||||
- Private
|
||||
* Extra features
|
||||
- Sphinx
|
||||
- SMS
|
||||
- Queues and daemons
|
||||
- Themes
|
||||
- Translation
|
||||
- Queues and daemons
|
||||
* After installation
|
||||
- Backups
|
||||
- Private
|
||||
- Upgrading
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
@ -41,10 +44,10 @@ functional setup of GNU Social:
|
||||
- php5-curl Fetching files by HTTP.
|
||||
- php5-gd Image manipulation (scaling).
|
||||
- php5-gmp For Salmon signatures (part of OStatus).
|
||||
- php5-intl Internationalization support, part of core.
|
||||
- php5-intl Internationalization support (transliteration et al).
|
||||
- php5-json For WebFinger lookups and more.
|
||||
- php5-mysqlnd The native driver for PHP5 MariaDB connections. If you
|
||||
use MySQL, 'mysql' or 'mysqli' may work.
|
||||
use MySQL, 'php5-mysql' or 'php5-mysqli' may be enough.
|
||||
|
||||
The above package names are for Debian based systems. In the case of
|
||||
Arch Linux, PHP is compiled with support for most extensions but they
|
||||
@ -69,7 +72,7 @@ For some functionality, you will also need the following extensions:
|
||||
|
||||
You may also experience better performance from your site if you configure
|
||||
a PHP cache/accelerator. Most distributions come with "opcache" support.
|
||||
Enable it in your php.ini, it is documented there together with its settings.
|
||||
Enable it in your php.ini where it is documented together with its settings.
|
||||
|
||||
Installation
|
||||
============
|
||||
@ -131,9 +134,9 @@ especially if you've previously installed PHP/MariaDB packages.
|
||||
writeable by the Web server group, as noted above.
|
||||
|
||||
5. Create a database to hold your site data. Something like this
|
||||
should work:
|
||||
should work (you will be prompted for your database password):
|
||||
|
||||
mysqladmin -u "root" --password="rootpassword" create gnusocial
|
||||
mysqladmin -u "root" -p create social
|
||||
|
||||
Note that GNU Social should have its own database; you should not share
|
||||
the database with another program. You can name it whatever you want,
|
||||
@ -147,17 +150,17 @@ especially if you've previously installed PHP/MariaDB packages.
|
||||
database. If you have shell access, this will probably work from the
|
||||
MariaDB shell:
|
||||
|
||||
GRANT ALL on gnusocial.*
|
||||
TO 'gnusocial'@'localhost'
|
||||
GRANT ALL on social.*
|
||||
TO 'social'@'localhost'
|
||||
IDENTIFIED BY 'agoodpassword';
|
||||
|
||||
You should change the user identifier 'gnusocial' and 'agoodpassword'
|
||||
You should change the user identifier 'social' and 'agoodpassword'
|
||||
to your preferred new database username and password. You may want to
|
||||
test logging in to MariaDB as this new user.
|
||||
|
||||
7. In a browser, navigate to the GNU Social install script; something like:
|
||||
|
||||
http://social.example.net/install.php
|
||||
https://social.example.net/install.php
|
||||
|
||||
Enter the database connection information and your site name. The
|
||||
install program will configure your site and install the initial,
|
||||
@ -171,55 +174,100 @@ Fancy URLs
|
||||
----------
|
||||
|
||||
By default, GNU Social will use URLs that include the main PHP program's
|
||||
name in them. For example, a user's home profile might be found at:
|
||||
name in them. For example, a user's home profile might be found at either
|
||||
of these URLS depending on the webserver's configuration and capabilities:
|
||||
|
||||
http://example.net/gnusocial/index.php/gnusocial/fred
|
||||
https://social.example.net/index.php/fred
|
||||
https://social.example.net/index.php?p=fred
|
||||
|
||||
On certain systems that don't support this kind of syntax, they'll
|
||||
look like this:
|
||||
It's possible to configure the software to use fancy URLs so it looks like
|
||||
this instead:
|
||||
|
||||
http://example.net/gnusocial/index.php?p=gnusocial/fred
|
||||
|
||||
It's possible to configure the software so it looks like this instead:
|
||||
|
||||
http://example.net/gnusocial/fred
|
||||
https://social.example.net/fred
|
||||
|
||||
These "fancy URLs" are more readable and memorable for users. To use
|
||||
fancy URLs, you must either have Apache 2.x with .htaccess enabled and
|
||||
mod_rewrite enabled, -OR- know how to configure "url redirection" in
|
||||
your server (like lighttpd or nginx).
|
||||
|
||||
1. Copy the htaccess.sample file to .htaccess in your StatusNet
|
||||
directory.
|
||||
|
||||
2. Change the "RewriteBase" in the new .htaccess file to be the URL path
|
||||
to your GNU Social installation on your server. Typically this will
|
||||
be the path to your GNU Social directory relative to your Web root.
|
||||
If you are installing it in the root directory, leave it as '/'.
|
||||
|
||||
3. Add, uncomment or change a line in your config.php file so it says:
|
||||
1. See the instructions for each respective webserver software:
|
||||
* For Apache, inspect the "htaccess.sample" file and save it as
|
||||
".htaccess" after making any necessary modifications. Our sample
|
||||
file is well commented.
|
||||
* For lighttpd, inspect the lighttpd.conf.example file and apply the
|
||||
appropriate changes in your virtualhost configuration for lighttpd.
|
||||
* For nginx and other webservers, we gladly accept contributions of
|
||||
server configuration examples.
|
||||
|
||||
2. Assuming your webserver is properly configured and have its settings
|
||||
applied (remember to reload/restart it), you can add this to your
|
||||
GNU social's config.php file:
|
||||
$config['site']['fancy'] = true;
|
||||
|
||||
You should now be able to navigate to a "fancy" URL on your server,
|
||||
like:
|
||||
|
||||
http://example.net/gnusocial/main/register
|
||||
https://social.example.net/main/register
|
||||
|
||||
If you changed your HTTP server configuration, you may need to restart
|
||||
the server first.
|
||||
Themes
|
||||
------
|
||||
|
||||
If it doesn't work, double-check that AllowOverride for the GNU Social
|
||||
directory is 'All' in your Apache configuration file. This is usually
|
||||
/etc/httpd.conf, /etc/apache/httpd.conf, or (on Debian and Ubuntu)
|
||||
/etc/apache2/sites-available/default. See the Apache documentation for
|
||||
.htaccess files for more details:
|
||||
As of right now, your ability change the theme is limited to CSS
|
||||
stylesheets and some image files; you can't change the HTML output,
|
||||
like adding or removing menu items, without the help of a plugin.
|
||||
|
||||
http://httpd.apache.org/docs/2.2/howto/htaccess.html
|
||||
You can choose a theme using the $config['site']['theme'] element in
|
||||
the config.php file. See below for details.
|
||||
|
||||
Also, check that mod_rewrite is installed and enabled:
|
||||
You can add your own theme by making a sub-directory of the 'theme'
|
||||
subdirectory with the name of your theme. Each theme can have the
|
||||
following files:
|
||||
|
||||
http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html
|
||||
display.css: a CSS2 file for "default" styling for all browsers.
|
||||
logo.png: a logo image for the site.
|
||||
default-avatar-profile.png: a 96x96 pixel image to use as the avatar for
|
||||
users who don't upload their own.
|
||||
default-avatar-stream.png: Ditto, but 48x48. For streams of notices.
|
||||
default-avatar-mini.png: Ditto ditto, but 24x24. For subscriptions
|
||||
listing on profile pages.
|
||||
|
||||
You may want to start by copying the files from the default theme to
|
||||
your own directory.
|
||||
|
||||
Private
|
||||
-------
|
||||
|
||||
A GNU social node can be configured as "private", which means it will not
|
||||
federate with other nodes in the network. It is not a recommended method
|
||||
of using GNU social and we cannot at the current state of development
|
||||
guarantee that there are no leaks (what a public network sees as features,
|
||||
private sites will likely see as bugs).
|
||||
|
||||
Private nodes are however an easy way to easily setup collaboration and
|
||||
image sharing within a workgroup or a smaller community where federation
|
||||
is not a desired feature. Also, it is possible to change this setting and
|
||||
instantly gain full federation features.
|
||||
|
||||
Access to file attachments can also be restricted to logged-in users only:
|
||||
|
||||
1. Add a directory outside the web root where your file uploads will be
|
||||
stored. Use this command as an initial guideline to create it:
|
||||
|
||||
mkdir /var/www/gnusocial-files
|
||||
|
||||
2. Make the file uploads directory writeable by the web server. An
|
||||
insecure way to do this is (to do it properly, read up on UNIX file
|
||||
permissions and configure your webserver accordingly):
|
||||
|
||||
chmod a+x /var/www/gnusocial-files
|
||||
|
||||
3. Tell GNU social to use this directory for file uploads. Add a line
|
||||
like this to your config.php:
|
||||
|
||||
$config['attachments']['dir'] = '/var/www/gnusocial-files';
|
||||
|
||||
Extra features
|
||||
==============
|
||||
|
||||
Sphinx
|
||||
------
|
||||
@ -284,7 +332,21 @@ For this to work, there *must* be a domain or sub-domain for which all
|
||||
|
||||
$config['mail']['domain'] = 'yourdomain.example.net';
|
||||
|
||||
Translations
|
||||
------------
|
||||
|
||||
For info on helping with translations, see the platform currently in use
|
||||
for translations: https://www.transifex.com/projects/p/gnu-social/
|
||||
|
||||
Translations use the gettext system <http://www.gnu.org/software/gettext/>.
|
||||
If you for some reason do not wish to sign up to the Transifex service,
|
||||
you can review the files in the "locale/" sub-directory of GNU social.
|
||||
Each plugin also has its own translation files.
|
||||
|
||||
To get your own site to use all the translated languages, and you are
|
||||
tracking the git repo, you will need to install at least 'gettext' on
|
||||
your system and then run:
|
||||
$ make translations
|
||||
|
||||
Queues and daemons
|
||||
------------------
|
||||
@ -346,16 +408,13 @@ separate server is probably a good idea for high-volume sites.
|
||||
.htaccess file, but make sure that your config.php file is close
|
||||
to, or identical to, your Web server's version.
|
||||
|
||||
3. In your config.php files (both the Web server and the queues
|
||||
server!), set the following variable:
|
||||
3. In your config.php files (on the server where you run the queue
|
||||
daemon), set the following variable:
|
||||
|
||||
$config['queue']['enabled'] = true;
|
||||
$config['queue']['daemon'] = true;
|
||||
|
||||
You may also want to look at the 'daemon' section of this file for
|
||||
more daemon options. Note that if you set the 'user' and/or 'group'
|
||||
options, you'll need to create that user and/or group by hand.
|
||||
They're not created automatically.
|
||||
You may also want to look at the 'Queues and Daemons' section in
|
||||
this file for more background processing options.
|
||||
|
||||
4. On the queues server, run the command scripts/startdaemons.sh.
|
||||
|
||||
@ -385,85 +444,20 @@ It is also possible to use a STOMP server instead of our kind of hacky
|
||||
home-grown DB-based queue solution. This is strongly recommended for
|
||||
best response time, especially when using XMPP.
|
||||
|
||||
Themes
|
||||
------
|
||||
|
||||
Older themes (version 0.9.x and below) no longer work with StatusNet
|
||||
1.0.x, due to major changes in the site layout. We ship with three new
|
||||
themes for this version, 'neo', 'neo-blue' and 'neo-light'.
|
||||
|
||||
As of right now, your ability to change the theme is site-wide; users
|
||||
can't choose their own theme. Additionally, the only thing you can
|
||||
change in the theme is CSS stylesheets and some image files; you can't
|
||||
change the HTML output, like adding or removing menu items.
|
||||
|
||||
You can choose a theme using the $config['site']['theme'] element in
|
||||
the config.php file. See below for details.
|
||||
|
||||
You can add your own theme by making a sub-directory of the 'theme'
|
||||
subdirectory with the name of your theme. Each theme can have the
|
||||
following files:
|
||||
|
||||
display.css: a CSS2 file for "default" styling for all browsers.
|
||||
logo.png: a logo image for the site.
|
||||
default-avatar-profile.png: a 96x96 pixel image to use as the avatar for
|
||||
users who don't upload their own.
|
||||
default-avatar-stream.png: Ditto, but 48x48. For streams of notices.
|
||||
default-avatar-mini.png: Ditto ditto, but 24x24. For subscriptions
|
||||
listing on profile pages.
|
||||
|
||||
You may want to start by copying the files from the default theme to
|
||||
your own directory.
|
||||
|
||||
Translation
|
||||
-----------
|
||||
|
||||
Translations in StatusNet use the gettext system <http://www.gnu.org/software/gettext/>.
|
||||
Theoretically, you can add your own sub-directory to the locale/
|
||||
subdirectory to add a new language to your system. You'll need to
|
||||
compile the ".po" files into ".mo" files, however.
|
||||
|
||||
Contributions of translation information to StatusNet are very easy:
|
||||
you can use the Web interface at translatewiki.net to add one
|
||||
or a few or lots of new translations -- or even new languages. You can
|
||||
also download more up-to-date .po files there, if you so desire.
|
||||
|
||||
For info on helping with translations, see http://status.net/wiki/Translations
|
||||
After installation
|
||||
==================
|
||||
|
||||
Backups
|
||||
-------
|
||||
|
||||
There is no built-in system for doing backups in StatusNet. You can make
|
||||
There is no built-in system for doing backups in GNU social. You can make
|
||||
backups of a working StatusNet system by backing up the database and
|
||||
the Web directory. To backup the database use mysqldump <http://ur1.ca/7xo>
|
||||
the Web directory. To backup the database use mysqldump <https://mariadb.com/kb/en/mariadb/mysqldump/>
|
||||
and to backup the Web directory, try tar.
|
||||
|
||||
Private
|
||||
-------
|
||||
Upgrading
|
||||
---------
|
||||
|
||||
The administrator can set the "private" flag for a site so that it's
|
||||
not visible to non-logged-in users. (This is the default for new installs of version 1.0!)
|
||||
|
||||
This might be useful for workgroups who want to share a social
|
||||
networking site for project management, but host it on a public
|
||||
server.
|
||||
|
||||
Total privacy is attempted but not guaranteed or ensured. Private sites
|
||||
currently don't work well with OStatus federation.
|
||||
|
||||
Access to file attachments can also be restricted to logged-in users only.
|
||||
|
||||
1. Add a directory outside the web root where your file uploads will be
|
||||
stored. Usually a command like this will work:
|
||||
|
||||
mkdir /var/www/statusnet-files
|
||||
|
||||
2. Make the file uploads directory writeable by the web server. An
|
||||
insecure way to do this is:
|
||||
|
||||
chmod a+x /var/www/statusnet-files
|
||||
|
||||
3. Tell StatusNet to use this directory for file uploads. Add a line
|
||||
like this to your config.php:
|
||||
|
||||
$config['attachments']['dir'] = '/var/www/statusnet-files';
|
||||
Upgrading is strongly recommended to stay up to date with security fixes
|
||||
and new features. For instructions on how to upgrade GNU social code,
|
||||
please see the UPGRADE file.
|
||||
|
16
README.md
@ -1,5 +1,5 @@
|
||||
# GNU social 1.1.3
|
||||
February 2015-02-27
|
||||
# GNU social 1.2.x
|
||||
2015
|
||||
|
||||
(c) Free Software Foundation, Inc
|
||||
(c) StatusNet, Inc
|
||||
@ -100,15 +100,19 @@ for additional terms.
|
||||
|
||||
## New this version
|
||||
|
||||
This is a security fix and bug fix release since 1.1.3-beta2.
|
||||
All 1.1.x sites should upgrade to this version.
|
||||
This is the development branch for the 1.2.x version of GNU social.
|
||||
All daring 1.1.x admins should upgrade to this version.
|
||||
|
||||
So far it includes the following changes:
|
||||
|
||||
- Backing up a user's account is more and more complete.
|
||||
- Emojis 😸 (utf8mb4 support)
|
||||
|
||||
The last release, 1.1.3, gave us these improvements:
|
||||
|
||||
- XSS security fix (thanks Simon Waters, <https://www.surevine.com/>)
|
||||
- Many improvements to ease adoption of the Qvitter front-end <https://github.com/hannesmannerheim/qvitter>
|
||||
- Protocol adaptions for improved performance and stability
|
||||
- Backing up a user's account now appears to work as it should
|
||||
|
||||
Upgrades from _StatusNet_ 1.1.1 will also experience these improvements:
|
||||
|
||||
@ -146,7 +150,7 @@ In the current phase of development it is probably
|
||||
recommended to use git as a means to stay up to date
|
||||
with the source code. You can choose between these
|
||||
branches:
|
||||
- 1.1.x "stable", few updates, well tested code
|
||||
- 1.2.x "stable", few updates, well tested code
|
||||
- master "testing", more updates, usually working well
|
||||
- nightly "unstable", most updates, not always working
|
||||
|
||||
|
143
UPGRADE
@ -1,26 +1,77 @@
|
||||
Upgrading
|
||||
=========
|
||||
|
||||
StatusNet 1.1.1 to GNU social
|
||||
-----------------------------
|
||||
GNU social 1.1.x to GNU social 1.2.x
|
||||
------------------------------------
|
||||
|
||||
If you are tracking the GNU social git repository, we currently recommend
|
||||
using the "master" branch (or nightly if you want to use latest features)
|
||||
and follow this procedure:
|
||||
|
||||
0. Backup your data. The StatusNet upgrade discussions below have some
|
||||
guidelines to back up the database and files (mysqldump and rsync).
|
||||
|
||||
1. Stop your queue daemons (you can run this command even if you do not
|
||||
use the queue daemons):
|
||||
$ bash scripts/stopdaemons.sh
|
||||
|
||||
2. Run the command to fetch the latest sourcecode:
|
||||
$ git pull
|
||||
|
||||
If you are not using git we recommend following the instructions below
|
||||
for upgrading "StatusNet 1.1.x to GNU social 1.2.x" as they are similar.
|
||||
|
||||
3. Run the upgrade script:
|
||||
$ php scripts/upgrade.php
|
||||
|
||||
The upgrade script will likely take a long time because it will
|
||||
upgrade the tables to another character encoding and make other
|
||||
automated upgrades. Make sure it ends without errors. If you get
|
||||
errors, create a new task on https://bugz.foocorp.net/
|
||||
|
||||
4. Start your queue daemons again (you can run this command even if you
|
||||
do not use the queue daemons):
|
||||
$ bash scripts/startdaemons.sh
|
||||
|
||||
5. Report any issues at https://bugz.foocorp.net/ (tag GNU social)
|
||||
|
||||
If you are using ssh keys to log in to your server, you can make this
|
||||
procedure pretty painless (assuming you have automated backups already).
|
||||
Make sure you "cd" into the correct directory (in this case "htdocs")
|
||||
and use the correct login@hostname combo:
|
||||
$ ssh social@domain.example 'cd htdocs
|
||||
&& bash scripts/stopdaemons.sh
|
||||
&& git pull
|
||||
&& time php scripts/upgrade.php
|
||||
&& bash scripts/startdaemons.sh'
|
||||
|
||||
StatusNet 1.1.x to GNU social 1.2.x
|
||||
-----------------------------------
|
||||
|
||||
We cannot support migrating from any other version of StatusNet than
|
||||
1.1.1. If you are running a StatusNet version lower than this, please
|
||||
follow the upgrade procedures for each respective StatusNet version.
|
||||
|
||||
You are now running StatusNet 1.1.1 and want to migrate to GNU social.
|
||||
Beware there may be changes in minimum required version of PHP and the
|
||||
modules used, so double-check the INSTALL file's requirements list.
|
||||
You are now running StatusNet 1.1.1 and want to migrate to GNU social
|
||||
1.2.x. Beware there may be changes in minimum required version of PHP
|
||||
and the modules required, so review the INSTALL file (php5-intl is a
|
||||
newly added dependency for example).
|
||||
|
||||
Before you begin: Make backups. Always make backups. Of your entire
|
||||
* Before you begin: Make backups. Always make backups. Of your entire
|
||||
directory structure and the database too. All tables. All data. Alles.
|
||||
|
||||
0. Stop your queue daemons 'php scripts/stopdaemon.php' should do it.
|
||||
0. Make a backup of everything. To backup the database, you can use a
|
||||
variant of this command (you will be prompted for the database password):
|
||||
$ mysqldump -u dbuser -p dbname > social-backup.sql
|
||||
|
||||
1. Stop your queue daemons 'bash scripts/stopdaemons.sh' should do it.
|
||||
Not everyone runs queue daemons, but the above command won't hurt.
|
||||
|
||||
1. Unpack your GNU social code to a fresh directory.
|
||||
2. Unpack your GNU social code to a fresh directory. You can do this
|
||||
by cloning our git repository:
|
||||
$ git clone https://gitorious.org/social/mainline.git gnusocial
|
||||
|
||||
2. Synchronize your local files to the GNU social directory. These
|
||||
3. Synchronize your local files to the GNU social directory. These
|
||||
will be the local files such as avatars, config and files:
|
||||
|
||||
avatar/*
|
||||
@ -30,70 +81,18 @@ directory structure and the database too. All tables. All data. Alles.
|
||||
.htaccess
|
||||
config.php
|
||||
|
||||
3. Replace your old StatusNet directory with the new GNU social
|
||||
This command will point you in the right direction on how to do it:
|
||||
$ rsync -avP statusnet/{.htaccess,avatar,background,file,local,config.php} gnusocial/
|
||||
|
||||
4. Replace your old StatusNet directory with the new GNU social
|
||||
directory in your webserver root.
|
||||
|
||||
4. Run the upgrade script: 'php scripts/upgrade.php'
|
||||
5. Run the upgrade script: 'php scripts/upgrade.php'
|
||||
The upgrade script will likely take a long time because it will
|
||||
upgrade the tables to another character encoding and make other
|
||||
automated upgrades. Make sure it ends without errors. If you get
|
||||
errors, create a new task on https://bugz.foocorp.net/
|
||||
|
||||
5. Start your queue daemons: 'php scripts/startdaemons.php'
|
||||
6. Start your queue daemons: 'bash scripts/startdaemons.sh'
|
||||
|
||||
6. Report any issues at https://bugz.foocorp.net/ (tag GNU social)
|
||||
|
||||
|
||||
Legacy StatusNet instructions
|
||||
-----------------------------
|
||||
|
||||
These instructions are here for historical and perhaps informational
|
||||
purposes.
|
||||
|
||||
If you've been using StatusNet 1.0 or lower, or if you've
|
||||
been tracking the "git" version of the software, you will probably
|
||||
want to upgrade and keep your existing data. Try these step-by-step
|
||||
instructions; read to the end first before trying them.
|
||||
|
||||
0. Download StatusNet and set up all the prerequisites as if you were
|
||||
doing a new install.
|
||||
1. Make backups of both your database and your Web directory. UNDER NO
|
||||
CIRCUMSTANCES should you try to do an upgrade without a known-good
|
||||
backup. You have been warned.
|
||||
2. Shut down Web access to your site, either by turning off your Web
|
||||
server or by redirecting all pages to a "sorry, under maintenance"
|
||||
page.
|
||||
3. Shut down XMPP access to your site, typically by shutting down the
|
||||
xmppdaemon.php process and all other daemons that you're running.
|
||||
If you've got "monit" or "cron" automatically restarting your
|
||||
daemons, make sure to turn that off, too.
|
||||
4. Shut down SMS and email access to your site. The easy way to do
|
||||
this is to comment out the line piping incoming email to your
|
||||
maildaemon.php file, and running something like "newaliases".
|
||||
5. Once all writing processes to your site are turned off, make a
|
||||
final backup of the Web directory and database.
|
||||
6. Move your StatusNet directory to a backup spot, like "statusnet.bak".
|
||||
7. Unpack your StatusNet 1.1.1 tarball and move it to "statusnet" or
|
||||
wherever your code used to be.
|
||||
8. Copy the config.php file and the contents of the avatar/, background/,
|
||||
file/, and local/ subdirectories from your old directory to your new
|
||||
directory.
|
||||
9. Copy htaccess.sample to .htaccess in the new directory. Change the
|
||||
RewriteBase to use the correct path.
|
||||
10. Upgrade the database.
|
||||
|
||||
NOTE: this step is destructive and cannot be
|
||||
reversed. YOU CAN EASILY DESTROY YOUR SITE WITH THIS STEP. Don't
|
||||
do it without a known-good backup!
|
||||
|
||||
In your new StatusNet 1.1.1 directory and AFTER YOU MAKE A
|
||||
BACKUP run the upgrade.php script like this:
|
||||
|
||||
php ./scripts/upgrade.php
|
||||
|
||||
11. Use mysql or psql client to log into your database and make sure that
|
||||
the notice, user, profile, subscription etc. tables are non-empty.
|
||||
12. Turn back on the Web server, and check that things still work.
|
||||
13. Turn back on XMPP bots and email maildaemon.
|
||||
|
||||
NOTE: the 1.0.0 version of StatusNet changed the URLs for all admin
|
||||
panels from /admin/* to /panel/*. This now allows the (popular)
|
||||
username 'admin', but blocks the considerably less popular username
|
||||
'panel'. If you have an existing user named 'panel', you should rename
|
||||
them before upgrading.
|
||||
7. Report any issues at https://bugz.foocorp.net/ (tag GNU social)
|
||||
|
@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* StatusNet, the distributed open-source microblogging tool
|
||||
*
|
||||
@ -156,28 +155,23 @@ class ApiAccountRegisterAction extends ApiAction
|
||||
// annoy spammers
|
||||
sleep(7);
|
||||
|
||||
if ($user = User::register(array('nickname' => $nickname,
|
||||
try {
|
||||
$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);
|
||||
}
|
||||
|
||||
'code' => $this->code));
|
||||
Event::handle('EndRegistrationTry', array($this));
|
||||
|
||||
$this->initDocument('json');
|
||||
$this->showJsonObjects($this->twitterUserArray($user->getProfile()));
|
||||
$this->endDocument('json');
|
||||
|
||||
} else {
|
||||
// TRANS: Form validation error displayed when trying to register with an invalid username or password.
|
||||
$this->clientError(_('Invalid username or password.'), 400);
|
||||
} catch (Exception $e) {
|
||||
$this->clientError($e->getMessage(), 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,9 +29,7 @@
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
exit(1);
|
||||
}
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
/**
|
||||
* Allows the authenticating users to follow (subscribe) the user specified in
|
||||
@ -90,7 +88,7 @@ class ApiFriendshipsCreateAction extends ApiAuthAction
|
||||
$this->clientError(_('Could not follow user: profile not found.'), 403);
|
||||
}
|
||||
|
||||
if ($this->user->isSubscribed($this->other)) {
|
||||
if ($this->scoped->isSubscribed($this->other)) {
|
||||
$errmsg = sprintf(
|
||||
// TRANS: Client error displayed when trying to follow a user that's already being followed.
|
||||
// TRANS: %s is the nickname of the user that is already being followed.
|
||||
@ -101,9 +99,9 @@ class ApiFriendshipsCreateAction extends ApiAuthAction
|
||||
}
|
||||
|
||||
try {
|
||||
Subscription::start($this->user->getProfile(), $this->other);
|
||||
} catch (Exception $e) {
|
||||
$this->clientError($e->getMessage(), 403);
|
||||
Subscription::start($this->scoped, $this->other);
|
||||
} catch (AlreadyFulfilledException $e) {
|
||||
$this->clientError($e->getMessage(), 409);
|
||||
}
|
||||
|
||||
$this->initDocument($this->format);
|
||||
|
@ -29,9 +29,7 @@
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
exit(1);
|
||||
}
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
/**
|
||||
* Allows the authenticating users to unfollow (unsubscribe) the user specified in
|
||||
@ -48,7 +46,9 @@ if (!defined('STATUSNET')) {
|
||||
*/
|
||||
class ApiFriendshipsDestroyAction extends ApiAuthAction
|
||||
{
|
||||
var $other = null;
|
||||
protected $needPost = true;
|
||||
|
||||
protected $other = null;
|
||||
|
||||
/**
|
||||
* Take arguments for running
|
||||
@ -58,11 +58,10 @@ class ApiFriendshipsDestroyAction extends ApiAuthAction
|
||||
* @return boolean success flag
|
||||
*
|
||||
*/
|
||||
function prepare($args)
|
||||
protected function prepare(array $args=array())
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
$this->user = $this->auth_user;
|
||||
$this->other = $this->getTargetProfile($this->arg('id'));
|
||||
|
||||
return true;
|
||||
@ -73,58 +72,40 @@ class ApiFriendshipsDestroyAction extends ApiAuthAction
|
||||
*
|
||||
* Check the format and show the user info
|
||||
*
|
||||
* @param array $args $_REQUEST data (unused)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function handle($args)
|
||||
protected function handle()
|
||||
{
|
||||
parent::handle($args);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
|
||||
$this->clientError(
|
||||
// TRANS: Client error. POST is a HTTP command. It should not be translated.
|
||||
_('This method requires a POST.'),
|
||||
400,
|
||||
$this->format
|
||||
);
|
||||
return;
|
||||
}
|
||||
parent::handle();
|
||||
|
||||
if (!in_array($this->format, array('xml', 'json'))) {
|
||||
$this->clientError(
|
||||
// TRANS: Client error displayed when coming across a non-supported API method.
|
||||
_('API method not found.'),
|
||||
404,
|
||||
$this->format
|
||||
404
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($this->other)) {
|
||||
if (!$this->other instanceof Profile) {
|
||||
$this->clientError(
|
||||
// TRANS: Client error displayed when trying to unfollow a user that cannot be found.
|
||||
_('Could not unfollow user: User not found.'),
|
||||
403,
|
||||
$this->format
|
||||
403
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't allow unsubscribing from yourself!
|
||||
|
||||
if ($this->user->id == $this->other->id) {
|
||||
if ($this->scoped->id == $this->other->id) {
|
||||
$this->clientError(
|
||||
// TRANS: Client error displayed when trying to unfollow self.
|
||||
_("You cannot unfollow yourself."),
|
||||
403,
|
||||
$this->format
|
||||
403
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// throws an exception on error
|
||||
Subscription::cancel($this->user->getProfile(), $this->other);
|
||||
Subscription::cancel($this->scoped, $this->other);
|
||||
|
||||
$this->initDocument($this->format);
|
||||
$this->showProfile($this->other, $this->format);
|
||||
|
@ -29,9 +29,7 @@
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
exit(1);
|
||||
}
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
/**
|
||||
* Tests for the existence of friendship between two users. Will return true if
|
||||
@ -57,7 +55,7 @@ class ApiFriendshipsExistsAction extends ApiPrivateAuthAction
|
||||
*
|
||||
* @return boolean success flag
|
||||
*/
|
||||
function prepare($args)
|
||||
protected function prepare(array $args=array())
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
@ -72,22 +70,18 @@ class ApiFriendshipsExistsAction extends ApiPrivateAuthAction
|
||||
*
|
||||
* Check the format and show the user info
|
||||
*
|
||||
* @param array $args $_REQUEST data (unused)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function handle($args)
|
||||
protected function handle()
|
||||
{
|
||||
parent::handle($args);
|
||||
parent::handle();
|
||||
|
||||
if (empty($this->profile_a) || empty($this->profile_b)) {
|
||||
$this->clientError(
|
||||
// TRANS: Client error displayed when supplying invalid parameters to an API call checking if a friendship exists.
|
||||
_('Two valid IDs or nick names must be supplied.'),
|
||||
400,
|
||||
$this->format
|
||||
400
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$result = Subscription::exists($this->profile_a, $this->profile_b);
|
||||
|
@ -29,9 +29,7 @@
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
exit(1);
|
||||
}
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
/**
|
||||
* Outputs detailed information about the relationship between two users
|
||||
@ -56,7 +54,7 @@ class ApiFriendshipsShowAction extends ApiBareAuthAction
|
||||
*
|
||||
* @return boolean success flag
|
||||
*/
|
||||
function prepare($args)
|
||||
protected function prepare(array $args=array())
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
@ -109,13 +107,11 @@ class ApiFriendshipsShowAction extends ApiBareAuthAction
|
||||
*
|
||||
* Check the format and show the user info
|
||||
*
|
||||
* @param array $args $_REQUEST data (unused)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function handle($args)
|
||||
protected function handle()
|
||||
{
|
||||
parent::handle($args);
|
||||
parent::handle();
|
||||
|
||||
if (!in_array($this->format, array('xml', 'json'))) {
|
||||
// TRANS: Client error displayed when coming across a non-supported API method.
|
||||
|
@ -79,6 +79,10 @@ class ApiTimelineUserAction extends ApiBareAuthAction
|
||||
$this->clientError(_('No such user.'), 404);
|
||||
}
|
||||
|
||||
if (!$this->target->isLocal()) {
|
||||
$this->serverError(_('Remote user timelines are not available here yet.'), 501);
|
||||
}
|
||||
|
||||
$this->notices = $this->getNotices();
|
||||
|
||||
return true;
|
||||
@ -405,7 +409,7 @@ class ApiTimelineUserAction extends ApiBareAuthAction
|
||||
|
||||
// Get (safe!) HTML and text versions of the content
|
||||
|
||||
$rendered = $this->purify($sourceContent);
|
||||
$rendered = common_purify($sourceContent);
|
||||
$content = common_strip_html($rendered);
|
||||
|
||||
$shortened = $this->auth_user->shortenLinks($content);
|
||||
@ -504,13 +508,4 @@ class ApiTimelineUserAction extends ApiBareAuthAction
|
||||
|
||||
return $saved;
|
||||
}
|
||||
|
||||
function purify($content)
|
||||
{
|
||||
require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
|
||||
|
||||
$config = array('safe' => 1,
|
||||
'deny_attribute' => 'id,style,on*');
|
||||
return htmLawed($content, $config);
|
||||
}
|
||||
}
|
||||
|
@ -230,18 +230,11 @@ class AtompubsubscriptionfeedAction extends AtompubAction
|
||||
$this->clientError(sprintf(_('Unknown profile %s.'), $person->id));
|
||||
}
|
||||
|
||||
if (Subscription::exists($this->_profile, $profile)) {
|
||||
try {
|
||||
$sub = Subscription::start($this->_profile, $profile);
|
||||
} catch (AlreadyFulfilledException $e) {
|
||||
// 409 Conflict
|
||||
// TRANS: Client error displayed trying to subscribe to an already subscribed profile.
|
||||
// TRANS: %s is the profile the user already has a subscription on.
|
||||
$this->clientError(sprintf(_('Already subscribed to %s.'),
|
||||
$person->id),
|
||||
409);
|
||||
}
|
||||
|
||||
if (Subscription::start($this->_profile, $profile)) {
|
||||
$sub = Subscription::pkeyGet(array('subscriber' => $this->_profile->id,
|
||||
'subscribed' => $profile->id));
|
||||
$this->clientError($e->getMessage(), 409);
|
||||
}
|
||||
|
||||
Event::handle('EndAtomPubNewActivity', array($activity, $sub));
|
||||
|
@ -62,6 +62,6 @@ class Attachment_thumbnailAction extends AttachmentAction
|
||||
common_redirect($e->file->getUrl());
|
||||
}
|
||||
|
||||
common_redirect($thumbnail->getUrl());
|
||||
common_redirect(File_thumbnail::url($thumbnail->filename));
|
||||
}
|
||||
}
|
||||
|
@ -28,13 +28,7 @@
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET') && !defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
define('MAX_ORIGINAL', 480);
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
/**
|
||||
* Upload an avatar
|
||||
@ -369,13 +363,27 @@ class AvatarsettingsAction extends SettingsAction
|
||||
$dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0;
|
||||
$dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$file_d;
|
||||
$dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$file_d;
|
||||
$size = intval(min($dest_w, $dest_h, MAX_ORIGINAL));
|
||||
$size = intval(min($dest_w, $dest_h, common_config('avatar', 'maxsize')));
|
||||
|
||||
$box = array('width' => $size, 'height' => $size,
|
||||
'x' => $dest_x, 'y' => $dest_y,
|
||||
'w' => $dest_w, 'h' => $dest_h);
|
||||
|
||||
$user = common_current_user();
|
||||
$profile = $user->getProfile();
|
||||
|
||||
$imagefile = new ImageFile($user->id, $filedata['filepath']);
|
||||
$filename = $imagefile->resize($size, $dest_x, $dest_y, $dest_w, $dest_h);
|
||||
$imagefile = new ImageFile(null, $filedata['filepath']);
|
||||
$filename = Avatar::filename($profile->getID(), image_type_to_extension($imagefile->preferredType()),
|
||||
$size, common_timestamp());
|
||||
try {
|
||||
$imagefile->resizeTo(Avatar::path($filename), $box);
|
||||
} catch (UseFileAsThumbnailException $e) {
|
||||
common_debug('Using uploaded avatar directly without resizing, copying it to: '.$filename);
|
||||
if (!copy($filedata['filepath'], Avatar::path($filename))) {
|
||||
common_debug('Tried to copy image file '.$filedata['filepath'].' to destination '.Avatar::path($filename));
|
||||
throw new ServerException('Could not copy file to destination.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($profile->setOriginal($filename)) {
|
||||
@unlink($filedata['filepath']);
|
||||
|
@ -43,23 +43,17 @@ class CancelsubscriptionAction extends FormAction
|
||||
{
|
||||
protected $needPost = true;
|
||||
|
||||
protected function prepare(array $args=array())
|
||||
protected function doPreparation()
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
$profile_id = $this->int('unsubscribeto');
|
||||
$this->target = Profile::getKV('id', $profile_id);
|
||||
if (!$this->target instanceof Profile) {
|
||||
throw new NoProfileException($profile_id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function handlePost()
|
||||
protected function doPost()
|
||||
{
|
||||
parent::handlePost();
|
||||
|
||||
try {
|
||||
$request = Subscription_queue::pkeyGet(array('subscriber' => $this->scoped->id,
|
||||
'subscribed' => $this->target->id));
|
||||
@ -70,7 +64,7 @@ class CancelsubscriptionAction extends FormAction
|
||||
common_debug('Tried to cancel a non-existing pending subscription');
|
||||
}
|
||||
|
||||
if (StatusNet::isAjax()) {
|
||||
if (GNUsocial::isAjax()) {
|
||||
$this->startHTML('text/xml;charset=utf-8');
|
||||
$this->elementStart('head');
|
||||
// TRANS: Title after unsubscribing from a group.
|
||||
@ -82,10 +76,7 @@ class CancelsubscriptionAction extends FormAction
|
||||
$this->elementEnd('body');
|
||||
$this->endHTML();
|
||||
exit();
|
||||
} else {
|
||||
common_redirect(common_local_url('subscriptions',
|
||||
array('nickname' => $this->scoped->nickname)),
|
||||
303);
|
||||
}
|
||||
common_redirect(common_local_url('subscriptions', array('nickname' => $this->scoped->getNickname())), 303);
|
||||
}
|
||||
}
|
||||
|
@ -174,11 +174,15 @@ class DocNav extends Menu
|
||||
{
|
||||
function show()
|
||||
{
|
||||
if (Event::handle('StartDocNav', array($this))) {
|
||||
$stub = new HomeStubNav($this->action);
|
||||
$this->submenu(_m('MENU','Home'), $stub);
|
||||
|
||||
$docs = new DocListNav($this->action);
|
||||
$this->submenu(_m('MENU','Docs'), $docs);
|
||||
|
||||
Event::handle('EndDocNav', array($this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ class EmailsettingsAction extends SettingsAction
|
||||
*/
|
||||
function showContent()
|
||||
{
|
||||
$user = common_current_user();
|
||||
$user = $this->scoped->getUser();
|
||||
|
||||
$this->elementStart('form', array('method' => 'post',
|
||||
'id' => 'form_settings_email',
|
||||
@ -313,17 +313,15 @@ class EmailsettingsAction extends SettingsAction
|
||||
*/
|
||||
function savePreferences()
|
||||
{
|
||||
$user = common_current_user();
|
||||
$user = $this->scoped->getUser();
|
||||
|
||||
if (Event::handle('StartEmailSaveForm', array($this, $this->scoped))) {
|
||||
$emailnotifysub = $this->boolean('emailnotifysub');
|
||||
$emailnotifymsg = $this->boolean('emailnotifymsg');
|
||||
$emailnotifynudge = $this->boolean('emailnotifynudge');
|
||||
$emailnotifyattn = $this->boolean('emailnotifyattn');
|
||||
$emailmicroid = $this->boolean('emailmicroid');
|
||||
$emailpost = $this->boolean('emailpost');
|
||||
|
||||
assert(!is_null($user)); // should already be checked
|
||||
$emailnotifysub = $this->booleanintstring('emailnotifysub');
|
||||
$emailnotifymsg = $this->booleanintstring('emailnotifymsg');
|
||||
$emailnotifynudge = $this->booleanintstring('emailnotifynudge');
|
||||
$emailnotifyattn = $this->booleanintstring('emailnotifyattn');
|
||||
$emailmicroid = $this->booleanintstring('emailmicroid');
|
||||
$emailpost = $this->booleanintstring('emailpost');
|
||||
|
||||
$user->query('BEGIN');
|
||||
|
||||
@ -340,6 +338,7 @@ class EmailsettingsAction extends SettingsAction
|
||||
|
||||
if ($result === false) {
|
||||
common_log_db_error($user, 'UPDATE', __FILE__);
|
||||
$user->query('ROLLBACK');
|
||||
// TRANS: Server error thrown on database error updating e-mail preferences.
|
||||
$this->serverError(_('Could not update user.'));
|
||||
}
|
||||
|
@ -28,13 +28,7 @@
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET') && !defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
define('MAX_ORIGINAL', 480);
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
/**
|
||||
* Upload an avatar
|
||||
@ -390,13 +384,20 @@ class GrouplogoAction extends GroupAction
|
||||
$dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0;
|
||||
$dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$filedata['width'];
|
||||
$dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$filedata['height'];
|
||||
$size = min($dest_w, $dest_h);
|
||||
$size = ($size > MAX_ORIGINAL) ? MAX_ORIGINAL:$size;
|
||||
$size = min($dest_w, $dest_h, common_config('avatar', 'maxsize'));
|
||||
$box = array('width' => $size, 'height' => $size,
|
||||
'x' => $dest_x, 'y' => $dest_y,
|
||||
'w' => $dest_w, 'h' => $dest_h);
|
||||
|
||||
$imagefile = new ImageFile($this->group->id, $filedata['filepath']);
|
||||
$filename = $imagefile->resize($size, $dest_x, $dest_y, $dest_w, $dest_h);
|
||||
$profile = $this->group->getProfile();
|
||||
|
||||
if ($this->group->setOriginal($filename)) {
|
||||
$imagefile = new ImageFile(null, $filedata['filepath']);
|
||||
$filename = Avatar::filename($profile->getID(), image_type_to_extension($imagefile->preferredType()),
|
||||
$size, common_timestamp());
|
||||
|
||||
$imagefile->resizeTo(Avatar::path($filename), $box);
|
||||
|
||||
if ($profile->setOriginal($filename)) {
|
||||
@unlink($filedata['filepath']);
|
||||
unset($_SESSION['FILEDATA']);
|
||||
$this->mode = 'upload';
|
||||
|
@ -118,7 +118,7 @@ class InviteAction extends Action
|
||||
$this->already[] = $other;
|
||||
} else {
|
||||
try {
|
||||
Subscription::start($profile, $other);
|
||||
Subscription::ensureStart($profile, $other);
|
||||
$this->subbed[] = $other;
|
||||
} catch (Exception $e) {
|
||||
// subscription failed, but keep working
|
||||
|
@ -36,24 +36,6 @@ class LoginAction extends FormAction
|
||||
{
|
||||
protected $needLogin = false;
|
||||
|
||||
/**
|
||||
* Prepare page to run
|
||||
*
|
||||
*
|
||||
* @param $args
|
||||
* @return string title
|
||||
*/
|
||||
protected function prepare(array $args=array())
|
||||
{
|
||||
// @todo this check should really be in index.php for all sensitive actions
|
||||
$ssl = common_config('site', 'ssl');
|
||||
if (empty($_SERVER['HTTPS']) && ($ssl == 'always' || $ssl == 'sometimes')) {
|
||||
common_redirect(common_local_url('login'));
|
||||
}
|
||||
|
||||
return parent::prepare($args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle input, produce output
|
||||
*
|
||||
@ -79,10 +61,8 @@ class LoginAction extends FormAction
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handlePost()
|
||||
protected function doPost()
|
||||
{
|
||||
parent::handlePost();
|
||||
|
||||
// XXX: login throttle
|
||||
|
||||
$nickname = $this->trimmed('nickname');
|
||||
@ -122,22 +102,6 @@ class LoginAction extends FormAction
|
||||
common_redirect($url, 303);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store an error and show the page
|
||||
*
|
||||
* This used to show the whole page; now, it's just a wrapper
|
||||
* that stores the error in an attribute.
|
||||
*
|
||||
* @param string $error error, if any.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function showForm($msg=null, $success=false)
|
||||
{
|
||||
common_ensure_session();
|
||||
return parent::showForm($msg, $success);
|
||||
}
|
||||
|
||||
function showScripts()
|
||||
{
|
||||
parent::showScripts();
|
||||
@ -208,7 +172,7 @@ class LoginAction extends FormAction
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function getInstructions()
|
||||
protected function getInstructions()
|
||||
{
|
||||
if (common_logged_in() && !common_is_real_login() &&
|
||||
common_get_returnto()) {
|
||||
|
@ -28,9 +28,7 @@
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
exit(1);
|
||||
}
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
/**
|
||||
* Add a new application
|
||||
@ -51,10 +49,8 @@ class NewApplicationAction extends FormAction
|
||||
return _('New application');
|
||||
}
|
||||
|
||||
protected function handlePost()
|
||||
protected function doPost()
|
||||
{
|
||||
parent::handlePost();
|
||||
|
||||
if ($this->arg('cancel')) {
|
||||
common_redirect(common_local_url('oauthappssettings'), 303);
|
||||
} elseif ($this->arg('save')) {
|
||||
@ -65,27 +61,15 @@ class NewApplicationAction extends FormAction
|
||||
$this->clientError(_('Unexpected form submission.'));
|
||||
}
|
||||
|
||||
function showForm($msg=null)
|
||||
protected function getForm()
|
||||
{
|
||||
$this->msg = $msg;
|
||||
$this->showPage();
|
||||
return new ApplicationEditForm($this);
|
||||
}
|
||||
|
||||
function showContent()
|
||||
protected function getInstructions()
|
||||
{
|
||||
$form = new ApplicationEditForm($this);
|
||||
$form->show();
|
||||
}
|
||||
|
||||
function showPageNotice()
|
||||
{
|
||||
if ($this->msg) {
|
||||
$this->element('p', 'error', $this->msg);
|
||||
} else {
|
||||
$this->element('p', 'instructions',
|
||||
// TRANS: Form instructions for registering a new application.
|
||||
_('Use this form to register a new application.'));
|
||||
}
|
||||
return _('Use this form to register a new application.');
|
||||
}
|
||||
|
||||
private function trySave()
|
||||
@ -181,6 +165,7 @@ class NewApplicationAction extends FormAction
|
||||
|
||||
if (!$result) {
|
||||
common_log_db_error($consumer, 'INSERT', __FILE__);
|
||||
$app->query('ROLLBACK');
|
||||
// TRANS: Server error displayed when an application could not be registered in the database through the "New application" form.
|
||||
$this->serverError(_('Could not create application.'));
|
||||
}
|
||||
@ -191,9 +176,9 @@ class NewApplicationAction extends FormAction
|
||||
|
||||
if (!$this->app_id) {
|
||||
common_log_db_error($app, 'INSERT', __FILE__);
|
||||
$app->query('ROLLBACK');
|
||||
// TRANS: Server error displayed when an application could not be registered in the database through the "New application" form.
|
||||
$this->serverError(_('Could not create application.'));
|
||||
$app->query('ROLLBACK');
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -29,9 +29,7 @@
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
exit(1);
|
||||
}
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
/**
|
||||
* Add a new group
|
||||
@ -48,6 +46,8 @@ class NewgroupAction extends FormAction
|
||||
{
|
||||
protected $group;
|
||||
|
||||
protected $form = 'GroupEdit';
|
||||
|
||||
function getGroup() {
|
||||
return $this->group;
|
||||
}
|
||||
@ -58,39 +58,23 @@ class NewgroupAction extends FormAction
|
||||
return _('New group');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare to run
|
||||
*/
|
||||
protected function prepare(array $args=array())
|
||||
protected function doPreparation()
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
// $this->scoped is the current user profile
|
||||
if (!$this->scoped->hasRight(Right::CREATEGROUP)) {
|
||||
// TRANS: Client exception thrown when a user tries to create a group while banned.
|
||||
$this->clientError(_('You are not allowed to create groups on this site.'), 403);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function showContent()
|
||||
protected function getInstructions()
|
||||
{
|
||||
$form = new GroupEditForm($this);
|
||||
$form->show();
|
||||
}
|
||||
|
||||
public function showInstructions()
|
||||
{
|
||||
$this->element('p', 'instructions',
|
||||
// TRANS: Form instructions for group create form.
|
||||
_('Use this form to create a new group.'));
|
||||
return _('Use this form to create a new group.');
|
||||
}
|
||||
|
||||
protected function handlePost()
|
||||
protected function doPost()
|
||||
{
|
||||
parent::handlePost();
|
||||
|
||||
if (Event::handle('StartGroupSaveForm', array($this))) {
|
||||
$nickname = Nickname::normalize($this->trimmed('newnickname'), true);
|
||||
|
||||
@ -133,7 +117,6 @@ class NewgroupAction extends FormAction
|
||||
'Too many aliases! Maximum %d allowed.',
|
||||
common_config('group', 'maxaliases')),
|
||||
common_config('group', 'maxaliases')));
|
||||
return;
|
||||
}
|
||||
|
||||
if ($private) {
|
||||
|
@ -30,9 +30,7 @@
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET')) {
|
||||
exit(1);
|
||||
}
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
/**
|
||||
* Action for posting new notices
|
||||
@ -62,6 +60,9 @@ class NewnoticeAction extends FormAction
|
||||
// TRANS: Page title after sending a notice.
|
||||
return _('Notice posted');
|
||||
}
|
||||
if ($this->int('inreplyto')) {
|
||||
return _m('TITLE', 'New reply');
|
||||
}
|
||||
// TRANS: Page title for sending a new notice.
|
||||
return _m('TITLE','New notice');
|
||||
}
|
||||
@ -80,7 +81,7 @@ class NewnoticeAction extends FormAction
|
||||
}
|
||||
|
||||
/**
|
||||
* This handlePost saves a new notice, based on arguments
|
||||
* This doPost saves a new notice, based on arguments
|
||||
*
|
||||
* If successful, will show the notice, or return an Ajax-y result.
|
||||
* If not, it will show an error message -- possibly Ajax-y.
|
||||
@ -90,17 +91,15 @@ class NewnoticeAction extends FormAction
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handlePost()
|
||||
protected function doPost()
|
||||
{
|
||||
parent::handlePost();
|
||||
|
||||
assert($this->scoped); // XXX: maybe an error instead...
|
||||
assert($this->scoped instanceof Profile); // XXX: maybe an error instead...
|
||||
$user = $this->scoped->getUser();
|
||||
$content = $this->trimmed('status_textarea');
|
||||
$options = array();
|
||||
Event::handle('StartSaveNewNoticeWeb', array($this, $user, &$content, &$options));
|
||||
|
||||
if (!$content) {
|
||||
if (empty($content)) {
|
||||
// TRANS: Client error displayed trying to send a notice without content.
|
||||
$this->clientError(_('No content!'));
|
||||
}
|
||||
@ -110,7 +109,7 @@ class NewnoticeAction extends FormAction
|
||||
$cmd = $inter->handle_command($user, $content);
|
||||
|
||||
if ($cmd) {
|
||||
if (StatusNet::isAjax()) {
|
||||
if (GNUsocial::isAjax()) {
|
||||
$cmd->execute(new AjaxWebChannel($this));
|
||||
} else {
|
||||
$cmd->execute(new WebChannel($this));
|
||||
@ -128,7 +127,7 @@ class NewnoticeAction extends FormAction
|
||||
Notice::maxContent()));
|
||||
}
|
||||
|
||||
$replyto = intval($this->trimmed('inreplyto'));
|
||||
$replyto = $this->int('inreplyto');
|
||||
if ($replyto) {
|
||||
$options['reply_to'] = $replyto;
|
||||
}
|
||||
@ -195,7 +194,7 @@ class NewnoticeAction extends FormAction
|
||||
|
||||
Event::handle('EndSaveNewNoticeWeb', array($this, $user, &$content_shortened, &$options));
|
||||
|
||||
if (!StatusNet::isAjax()) {
|
||||
if (!GNUsocial::isAjax()) {
|
||||
$url = common_local_url('shownotice', array('notice' => $this->stored->id));
|
||||
common_redirect($url, 303);
|
||||
}
|
||||
|
@ -82,8 +82,8 @@ class ProfilesettingsAction extends SettingsAction
|
||||
*/
|
||||
function showContent()
|
||||
{
|
||||
$user = common_current_user();
|
||||
$profile = $user->getProfile();
|
||||
$profile = $this->scoped;
|
||||
$user = $this->scoped->getUser();
|
||||
|
||||
$this->elementStart('form', array('method' => 'post',
|
||||
'id' => 'form_settings_profile',
|
||||
@ -104,7 +104,9 @@ class ProfilesettingsAction extends SettingsAction
|
||||
// TRANS: Tooltip for field label in form for profile settings.
|
||||
_('1-64 lowercase letters or numbers, no punctuation or spaces.'),
|
||||
null, false, // "name" (will be set to id), then "required"
|
||||
!common_config('profile', 'changenick') ? array('disabled'=>'disabled') : array());
|
||||
!common_config('profile', 'changenick')
|
||||
? array('disabled' => 'disabled', 'placeholder' => null)
|
||||
: array('placeholder' => null));
|
||||
$this->elementEnd('li');
|
||||
$this->elementStart('li');
|
||||
// TRANS: Field label in form for profile settings.
|
||||
@ -260,9 +262,9 @@ class ProfilesettingsAction extends SettingsAction
|
||||
$homepage = $this->trimmed('homepage');
|
||||
$bio = $this->trimmed('bio');
|
||||
$location = $this->trimmed('location');
|
||||
$autosubscribe = $this->boolean('autosubscribe');
|
||||
$autosubscribe = $this->booleanintstring('autosubscribe');
|
||||
$subscribe_policy = $this->trimmed('subscribe_policy');
|
||||
$private_stream = $this->boolean('private_stream');
|
||||
$private_stream = $this->booleanintstring('private_stream');
|
||||
$language = $this->trimmed('language');
|
||||
$timezone = $this->trimmed('timezone');
|
||||
$tagstring = $this->trimmed('tags');
|
||||
@ -398,7 +400,7 @@ class ProfilesettingsAction extends SettingsAction
|
||||
$orig = clone($prefs);
|
||||
}
|
||||
|
||||
$prefs->share_location = $this->boolean('sharelocation');
|
||||
$prefs->share_location = $this->booleanintstring('sharelocation');
|
||||
|
||||
if ($exists) {
|
||||
$result = $prefs->update($orig);
|
||||
|
@ -27,9 +27,7 @@
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET') && !defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
|
||||
|
||||
/**
|
||||
* An action for registering a new user account
|
||||
@ -229,19 +227,16 @@ class RegisterAction extends Action
|
||||
} else if ($password != $confirm) {
|
||||
// TRANS: Form validation error displayed when trying to register with non-matching passwords.
|
||||
$this->showForm(_('Passwords do not match.'));
|
||||
} else if ($user = User::register(array('nickname' => $nickname,
|
||||
} else {
|
||||
try {
|
||||
$user = User::register(array('nickname' => $nickname,
|
||||
'password' => $password,
|
||||
'email' => $email,
|
||||
'fullname' => $fullname,
|
||||
'homepage' => $homepage,
|
||||
'bio' => $bio,
|
||||
'location' => $location,
|
||||
'code' => $code))) {
|
||||
if (!($user instanceof User)) {
|
||||
// TRANS: Form validation error displayed when trying to register with an invalid username or password.
|
||||
$this->showForm(_('Invalid username or password.'));
|
||||
return;
|
||||
}
|
||||
'code' => $code));
|
||||
// success!
|
||||
if (!common_set_user($user)) {
|
||||
// TRANS: Server error displayed when saving fails during user registration.
|
||||
@ -260,9 +255,10 @@ class RegisterAction extends Action
|
||||
Event::handle('EndRegistrationTry', array($this));
|
||||
|
||||
$this->showSuccess();
|
||||
} else {
|
||||
} catch (Exception $e) {
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,13 +27,7 @@
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET') && !defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once INSTALLDIR.'/lib/personalgroupnav.php';
|
||||
require_once INSTALLDIR.'/lib/noticelist.php';
|
||||
require_once INSTALLDIR.'/lib/feedlist.php';
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
/**
|
||||
* List of replies
|
||||
@ -44,72 +38,42 @@ require_once INSTALLDIR.'/lib/feedlist.php';
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://status.net/
|
||||
*/
|
||||
class RepliesAction extends Action
|
||||
class RepliesAction extends ManagedAction
|
||||
{
|
||||
var $page = null;
|
||||
var $notice;
|
||||
|
||||
/**
|
||||
* Prepare the object
|
||||
*
|
||||
* Check the input values and initialize the object.
|
||||
* Shows an error page on bad input.
|
||||
*
|
||||
* @param array $args $_REQUEST data
|
||||
*
|
||||
* @return boolean success flag
|
||||
*/
|
||||
function prepare($args)
|
||||
protected function doPreparation()
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
$nickname = common_canonical_nickname($this->arg('nickname'));
|
||||
|
||||
$this->user = User::getKV('nickname', $nickname);
|
||||
|
||||
if (!$this->user) {
|
||||
if (!$this->user instanceof User) {
|
||||
// TRANS: Client error displayed when trying to reply to a non-exsting user.
|
||||
$this->clientError(_('No such user.'));
|
||||
}
|
||||
|
||||
$profile = $this->user->getProfile();
|
||||
$this->target = $this->user->getProfile();
|
||||
|
||||
if (!$profile) {
|
||||
if (!$this->target instanceof Profile) {
|
||||
// TRANS: Error message displayed when referring to a user without a profile.
|
||||
$this->serverError(_('User has no profile.'));
|
||||
}
|
||||
|
||||
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
|
||||
$this->page = $this->int('page') ?: 1;
|
||||
|
||||
common_set_returnto($this->selfUrl());
|
||||
|
||||
$stream = new ReplyNoticeStream($this->user->id,
|
||||
Profile::current());
|
||||
$stream = new ReplyNoticeStream($this->target->getID(), $this->scoped);
|
||||
|
||||
$this->notice = $stream->getNotices(($this->page-1) * NOTICES_PER_PAGE,
|
||||
NOTICES_PER_PAGE + 1);
|
||||
|
||||
if($this->page > 1 && $this->notice->N == 0){
|
||||
if ($this->page > 1 && $this->notice->N == 0) {
|
||||
// TRANS: Client error when page not found (404)
|
||||
$this->clientError(_('No such page.'), 404);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a request
|
||||
*
|
||||
* Just show the page. All args already handled.
|
||||
*
|
||||
* @param array $args $_REQUEST data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function handle($args)
|
||||
{
|
||||
parent::handle($args);
|
||||
$this->showPage();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -124,12 +88,12 @@ class RepliesAction extends Action
|
||||
if ($this->page == 1) {
|
||||
// TRANS: Title for first page of replies for a user.
|
||||
// TRANS: %s is a user nickname.
|
||||
return sprintf(_("Replies to %s"), $this->user->nickname);
|
||||
return sprintf(_("Replies to %s"), $this->target->getNickname());
|
||||
} else {
|
||||
// TRANS: Title for all but the first page of replies for a user.
|
||||
// TRANS: %1$s is a user nickname, %2$d is a page number.
|
||||
return sprintf(_('Replies to %1$s, page %2$d'),
|
||||
$this->user->nickname,
|
||||
$this->target->getNickname(),
|
||||
$this->page);
|
||||
}
|
||||
}
|
||||
@ -144,7 +108,7 @@ class RepliesAction extends Action
|
||||
return array(new Feed(Feed::JSON,
|
||||
common_local_url('ApiTimelineMentions',
|
||||
array(
|
||||
'id' => $this->user->nickname,
|
||||
'id' => $this->target->getNickname(),
|
||||
'format' => 'as')),
|
||||
// TRANS: Link for feed with replies for a user.
|
||||
// TRANS: %s is a user nickname.
|
||||
@ -152,38 +116,31 @@ class RepliesAction extends Action
|
||||
$this->user->nickname)),
|
||||
new Feed(Feed::RSS1,
|
||||
common_local_url('repliesrss',
|
||||
array('nickname' => $this->user->nickname)),
|
||||
array('nickname' => $this->target->getNickname())),
|
||||
// TRANS: Link for feed with replies for a user.
|
||||
// TRANS: %s is a user nickname.
|
||||
sprintf(_('Replies feed for %s (RSS 1.0)'),
|
||||
$this->user->nickname)),
|
||||
$this->target->getNickname())),
|
||||
new Feed(Feed::RSS2,
|
||||
common_local_url('ApiTimelineMentions',
|
||||
array(
|
||||
'id' => $this->user->nickname,
|
||||
'id' => $this->target->getNickname(),
|
||||
'format' => 'rss')),
|
||||
// TRANS: Link for feed with replies for a user.
|
||||
// TRANS: %s is a user nickname.
|
||||
sprintf(_('Replies feed for %s (RSS 2.0)'),
|
||||
$this->user->nickname)),
|
||||
$this->target->getNickname())),
|
||||
new Feed(Feed::ATOM,
|
||||
common_local_url('ApiTimelineMentions',
|
||||
array(
|
||||
'id' => $this->user->nickname,
|
||||
'id' => $this->target->getNickname(),
|
||||
'format' => 'atom')),
|
||||
// TRANS: Link for feed with replies for a user.
|
||||
// TRANS: %s is a user nickname.
|
||||
sprintf(_('Replies feed for %s (Atom)'),
|
||||
$this->user->nickname)));
|
||||
$this->target->getNickname())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the content
|
||||
*
|
||||
* A list of notices that are replies to the user, plus pagination.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function showContent()
|
||||
{
|
||||
$nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE));
|
||||
@ -195,33 +152,30 @@ class RepliesAction extends Action
|
||||
|
||||
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
|
||||
$this->page, 'replies',
|
||||
array('nickname' => $this->user->nickname));
|
||||
array('nickname' => $this->target->getNickname()));
|
||||
}
|
||||
|
||||
function showEmptyListMessage()
|
||||
{
|
||||
// TRANS: Empty list message for page with replies for a user.
|
||||
// TRANS: %1$s and %s$s are the user nickname.
|
||||
$message = sprintf(_('This is the timeline showing replies to %1$s but %2$s hasn\'t received a notice to them yet.'),
|
||||
$this->user->nickname,
|
||||
$this->user->nickname) . ' ';
|
||||
// TRANS: %1$s is the user nickname.
|
||||
$message = sprintf(_('This is the timeline showing replies to %1$s but no notices have arrived yet.'), $this->target->getNickname());
|
||||
$message .= ' '; // Spacing between this sentence and the next.
|
||||
|
||||
if (common_logged_in()) {
|
||||
$current_user = common_current_user();
|
||||
if ($this->user->id === $current_user->id) {
|
||||
if ($this->target->getID() === $this->scoped->getID()) {
|
||||
// TRANS: Empty list message for page with replies for a user for the logged in user.
|
||||
// TRANS: This message contains a Markdown link in the form [link text](link).
|
||||
$message .= _('You can engage other users in a conversation, subscribe to more people or [join groups](%%action.groups%%).');
|
||||
} else {
|
||||
// TRANS: Empty list message for page with replies for a user for all logged in users but the user themselves.
|
||||
// TRANS: %1$s, %2$s and %3$s are a user nickname. This message contains a Markdown link in the form [link text](link).
|
||||
$message .= sprintf(_('You can try to [nudge %1$s](../%2$s) or [post something to them](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname);
|
||||
// TRANS: %1$s is a user nickname and %2$s is the same but with a prepended '@' character. This message contains a Markdown link in the form [link text](link).
|
||||
$message .= sprintf(_('You can try to [nudge %1$s](../%1$s) or [post something to them](%%%%action.newnotice%%%%?content=%2$s).'), $this->target->getNickname(), '@' . $this->target->getNickname());
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// TRANS: Empty list message for page with replies for a user for not logged in users.
|
||||
// TRANS: %1$s is a user nickname. This message contains a Markdown link in the form [link text](link).
|
||||
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->user->nickname);
|
||||
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->target->getNickname());
|
||||
}
|
||||
|
||||
$this->elementStart('div', 'guide');
|
||||
@ -229,7 +183,7 @@ class RepliesAction extends Action
|
||||
$this->elementEnd('div');
|
||||
}
|
||||
|
||||
function isReadOnly($args)
|
||||
public function isReadOnly($args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ class ShownoticeAction extends ManagedAction
|
||||
{
|
||||
parent::prepare($args);
|
||||
if ($this->boolean('ajax')) {
|
||||
StatusNet::setApi(true);
|
||||
GNUsocial::setApi(true);
|
||||
}
|
||||
|
||||
$this->notice = $this->getNotice();
|
||||
|
@ -28,15 +28,7 @@
|
||||
* @link http://status.net/
|
||||
*/
|
||||
|
||||
if (!defined('STATUSNET') && !defined('LACONICA')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once INSTALLDIR.'/lib/personalgroupnav.php';
|
||||
require_once INSTALLDIR.'/lib/noticelist.php';
|
||||
require_once INSTALLDIR.'/lib/profileminilist.php';
|
||||
require_once INSTALLDIR.'/lib/groupminilist.php';
|
||||
require_once INSTALLDIR.'/lib/feedlist.php';
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
/**
|
||||
* User profile page
|
||||
@ -233,10 +225,7 @@ class ShowstreamAction extends ProfileAction
|
||||
|
||||
function showNotices()
|
||||
{
|
||||
$pnl = null;
|
||||
if (Event::handle('ShowStreamNoticeList', array($this->notice, $this, &$pnl))) {
|
||||
$pnl = new ProfileNoticeList($this->notice, $this);
|
||||
}
|
||||
$pnl = new NoticeList($this->notice, $this);
|
||||
$cnt = $pnl->show();
|
||||
if (0 == $cnt) {
|
||||
$this->showEmptyListMessage();
|
||||
@ -293,57 +282,3 @@ class ShowstreamAction extends ProfileAction
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
|
||||
// We don't show the author for a profile, since we already know who it is!
|
||||
|
||||
/**
|
||||
* Slightly modified from standard list; the author & avatar are hidden
|
||||
* in CSS. We used to remove them here too, but as it turns out that
|
||||
* confuses the inline reply code... and we hide them in CSS anyway
|
||||
* since realtime updates come through in original form.
|
||||
*
|
||||
* Remaining customization right now is for the repeat marker, where
|
||||
* it'll list who the original poster was instead of who did the repeat
|
||||
* (since the repeater is you, and the repeatee isn't shown!)
|
||||
* This will remain inconsistent if realtime updates come through,
|
||||
* since those'll get rendered as a regular NoticeListItem.
|
||||
*/
|
||||
class ProfileNoticeList extends NoticeList
|
||||
{
|
||||
function newListItem($notice)
|
||||
{
|
||||
return new ProfileNoticeListItem($notice, $this->out);
|
||||
}
|
||||
}
|
||||
|
||||
class ProfileNoticeListItem extends DoFollowListItem
|
||||
{
|
||||
/**
|
||||
* show a link to the author of repeat
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function showRepeat()
|
||||
{
|
||||
if (!empty($this->repeat)) {
|
||||
|
||||
// FIXME: this code is almost identical to default; need to refactor
|
||||
|
||||
$attrs = array('href' => $this->profile->profileurl,
|
||||
'class' => 'url');
|
||||
|
||||
if (!empty($this->profile->fullname)) {
|
||||
$attrs['title'] = $this->profile->getFancyName();
|
||||
}
|
||||
|
||||
$this->out->elementStart('span', 'repeat');
|
||||
|
||||
$text_link = XMLStringer::estring('a', $attrs, $this->profile->nickname);
|
||||
|
||||
// TRANS: Link to the author of a repeated notice. %s is a linked nickname.
|
||||
$this->out->raw(sprintf(_('Repeat of %s'), $text_link));
|
||||
|
||||
$this->out->elementEnd('span');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ class SubscribeAction extends Action
|
||||
{
|
||||
// Throws exception on error
|
||||
|
||||
$sub = Subscription::start($this->user->getProfile(),
|
||||
$sub = Subscription::ensureStart($this->user->getProfile(),
|
||||
$this->other);
|
||||
|
||||
if ($this->boolean('ajax')) {
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
if (!defined('GNUSOCIAL')) { exit(1); }
|
||||
|
||||
require_once INSTALLDIR . '/lib/settingsaction.php';
|
||||
require_once INSTALLDIR . '/lib/peopletags.php';
|
||||
|
||||
class TagprofileAction extends FormAction
|
||||
@ -29,36 +28,32 @@ class TagprofileAction extends FormAction
|
||||
protected $target = null;
|
||||
protected $form = 'TagProfile';
|
||||
|
||||
protected function prepare(array $args=array())
|
||||
protected function doPreparation()
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
$id = $this->trimmed('id');
|
||||
if (!$id) {
|
||||
$this->target = null;
|
||||
} else {
|
||||
$uri = $this->trimmed('uri');
|
||||
if (!empty($id)) {
|
||||
$this->target = Profile::getKV('id', $id);
|
||||
|
||||
if (!$this->target instanceof Profile) {
|
||||
// TRANS: Client error displayed when referring to non-existing profile ID.
|
||||
$this->clientError(_('No profile with that ID.'));
|
||||
}
|
||||
} elseif (!empty($uri)) {
|
||||
$this->target = Profile::fromUri($uri);
|
||||
} else {
|
||||
// TRANS: Client error displayed when trying to tag a user but no ID or profile is provided.
|
||||
$this->clientError(_('No profile identifier provided.'));
|
||||
}
|
||||
|
||||
if ($this->target instanceof Profile && !$this->scoped->canTag($this->target)) {
|
||||
if (!$this->scoped->canTag($this->target)) {
|
||||
// TRANS: Client error displayed when trying to tag a user that cannot be tagged.
|
||||
$this->clientError(_('You cannot tag this user.'));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
$this->formOpts = $this->target;
|
||||
|
||||
protected function handle()
|
||||
{
|
||||
if (Event::handle('StartTagProfileAction', array($this, $this->target))) {
|
||||
parent::handle();
|
||||
Event::handle('EndTagProfileAction', array($this, $this->target));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function title()
|
||||
@ -115,17 +110,8 @@ class TagprofileAction extends FormAction
|
||||
}
|
||||
}
|
||||
|
||||
protected function getForm()
|
||||
protected function doPost()
|
||||
{
|
||||
$class = $this->form.'Form';
|
||||
$form = new $class($this, $this->target);
|
||||
return $form;
|
||||
}
|
||||
|
||||
protected function handlePost()
|
||||
{
|
||||
parent::handlePost(); // Does nothing for now
|
||||
|
||||
$tagstring = $this->trimmed('tags');
|
||||
$token = $this->trimmed('token');
|
||||
|
||||
@ -144,22 +130,16 @@ class TagprofileAction extends FormAction
|
||||
if (!common_valid_profile_tag($tag)) {
|
||||
// TRANS: Form validation error displayed if a given tag is invalid.
|
||||
// TRANS: %s is the invalid tag.
|
||||
$this->showForm(sprintf(_('Invalid tag: "%s".'), $tag));
|
||||
return;
|
||||
throw new ClientException(sprintf(_('Invalid tag: "%s".'), $tag));
|
||||
}
|
||||
|
||||
$tag_priv[$tag] = $private;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$result = Profile_tag::setTags($this->scoped->id, $this->target->id, $tags, $tag_priv);
|
||||
$result = Profile_tag::setTags($this->scoped->getID(), $this->target->getID(), $tags, $tag_priv);
|
||||
if (!$result) {
|
||||
throw new Exception('The tags could not be saved.');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->showForm($e->getMessage());
|
||||
return false;
|
||||
throw new ServerException('The tags could not be saved.');
|
||||
}
|
||||
|
||||
if ($this->boolean('ajax')) {
|
||||
@ -188,17 +168,4 @@ class TagprofileAction extends FormAction
|
||||
Event::handle('EndSavePeopletags', array($this, $tagstring));
|
||||
}
|
||||
}
|
||||
|
||||
function showPageNotice()
|
||||
{
|
||||
if ($this->error) {
|
||||
$this->element('p', 'error', $this->error);
|
||||
} else {
|
||||
$this->elementStart('div', 'instructions');
|
||||
$this->element('p', null,
|
||||
// TRANS: Page notice.
|
||||
_('Use this form to add your subscribers or subscriptions to lists.'));
|
||||
$this->elementEnd('div');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ class Attention extends Managed_DataObject
|
||||
public $__table = 'attention'; // table name
|
||||
public $notice_id; // int(4) primary_key not_null
|
||||
public $profile_id; // int(4) primary_key not_null
|
||||
public $reason; // varchar(255)
|
||||
public $reason; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime() not_null
|
||||
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
@ -33,7 +33,7 @@ class Attention extends Managed_DataObject
|
||||
'fields' => array(
|
||||
'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice_id to give attention'),
|
||||
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'profile_id for feed receiver'),
|
||||
'reason' => array('type' => 'varchar', 'length' => 255, 'description' => 'Optional reason why this was brought to the attention of profile_id'),
|
||||
'reason' => array('type' => 'varchar', 'length' => 191, 'description' => 'Optional reason why this was brought to the attention of profile_id'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
),
|
||||
|
@ -15,8 +15,8 @@ class Avatar extends Managed_DataObject
|
||||
public $width; // int(4) primary_key not_null
|
||||
public $height; // int(4) primary_key not_null
|
||||
public $mediatype; // varchar(32) not_null
|
||||
public $filename; // varchar(255)
|
||||
public $url; // varchar(255) unique_key
|
||||
public $filename; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $url; // varchar(191) unique_key not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime() not_null
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
@ -32,14 +32,14 @@ class Avatar extends Managed_DataObject
|
||||
'width' => array('type' => 'int', 'not null' => true, 'description' => 'image width'),
|
||||
'height' => array('type' => 'int', 'not null' => true, 'description' => 'image height'),
|
||||
'mediatype' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'file type'),
|
||||
'filename' => array('type' => 'varchar', 'length' => 255, 'description' => 'local filename, if local'),
|
||||
'url' => array('type' => 'varchar', 'length' => 255, 'description' => 'avatar location'),
|
||||
'filename' => array('type' => 'varchar', 'length' => 191, 'description' => 'local filename, if local'),
|
||||
'url' => array('type' => 'text', 'description' => 'avatar location, not indexed - do not use in WHERE statement'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
),
|
||||
'primary key' => array('profile_id', 'width', 'height'),
|
||||
'unique keys' => array(
|
||||
'avatar_url_key' => array('url'),
|
||||
// 'avatar_filename_key' => array('filename'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'avatar_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
|
||||
@ -241,16 +241,21 @@ class Avatar extends Managed_DataObject
|
||||
// TRANS: An error message when avatar size is unreasonable
|
||||
throw new Exception(_m('Avatar size too large'));
|
||||
}
|
||||
// So far we only have square avatars and I don't have time to
|
||||
// rewrite support for non-square ones right now ;)
|
||||
$height = $width;
|
||||
|
||||
$original = Avatar::getUploaded($target);
|
||||
|
||||
$imagefile = new ImageFile($target->id, Avatar::path($original->filename));
|
||||
$filename = $imagefile->resize($width);
|
||||
$imagefile = new ImageFile(null, Avatar::path($original->filename));
|
||||
$filename = Avatar::filename($target->getID(), image_type_to_extension($imagefile->preferredType()),
|
||||
$width, common_timestamp());
|
||||
$imagefile->resizeTo(Avatar::path($filename), array('width'=>$width, 'height'=>$height));
|
||||
|
||||
$scaled = clone($original);
|
||||
$scaled->original = false;
|
||||
$scaled->width = $width;
|
||||
$scaled->height = $width;
|
||||
$scaled->height = $height;
|
||||
$scaled->url = Avatar::url($filename);
|
||||
$scaled->filename = $filename;
|
||||
$scaled->created = common_sql_now();
|
||||
|
@ -35,7 +35,7 @@ class Config extends Managed_DataObject
|
||||
public $__table = 'config'; // table name
|
||||
public $section; // varchar(32) primary_key not_null
|
||||
public $setting; // varchar(32) primary_key not_null
|
||||
public $value; // varchar(255)
|
||||
public $value; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
|
||||
/* the code above is auto generated do not remove the tag below */
|
||||
###END_AUTOCODE
|
||||
@ -46,7 +46,7 @@ class Config extends Managed_DataObject
|
||||
'fields' => array(
|
||||
'section' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'default' => '', 'description' => 'configuration section'),
|
||||
'setting' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'default' => '', 'description' => 'configuration setting'),
|
||||
'value' => array('type' => 'varchar', 'length' => 255, 'description' => 'configuration value'),
|
||||
'value' => array('type' => 'varchar', 'length' => 191, 'description' => 'configuration value'),
|
||||
),
|
||||
'primary key' => array('section', 'setting'),
|
||||
);
|
||||
|
@ -12,8 +12,8 @@ class Confirm_address extends Managed_DataObject
|
||||
public $__table = 'confirm_address'; // table name
|
||||
public $code; // varchar(32) primary_key not_null
|
||||
public $user_id; // int(4) not_null
|
||||
public $address; // varchar(255) not_null
|
||||
public $address_extra; // varchar(255) not_null
|
||||
public $address; // varchar(191) not_null not 255 because utf8mb4 takes more space
|
||||
public $address_extra; // varchar(191) not_null not 255 because utf8mb4 takes more space
|
||||
public $address_type; // varchar(8) not_null
|
||||
public $claimed; // datetime()
|
||||
public $sent; // datetime()
|
||||
@ -28,8 +28,8 @@ class Confirm_address extends Managed_DataObject
|
||||
'fields' => array(
|
||||
'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'good random code'),
|
||||
'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user who requested confirmation'),
|
||||
'address' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'address (email, xmpp, SMS, etc.)'),
|
||||
'address_extra' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'carrier ID, for SMS'),
|
||||
'address' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'address (email, xmpp, SMS, etc.)'),
|
||||
'address_extra' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'carrier ID, for SMS'),
|
||||
'address_type' => array('type' => 'varchar', 'length' => 8, 'not null' => true, 'description' => 'address type ("email", "xmpp", "sms")'),
|
||||
'claimed' => array('type' => 'datetime', 'description' => 'date this was claimed for queueing'),
|
||||
'sent' => array('type' => 'datetime', 'description' => 'date this was sent for queueing'),
|
||||
|
@ -10,8 +10,8 @@ class Consumer extends Managed_DataObject
|
||||
/* the code below is auto generated do not remove the above tag */
|
||||
|
||||
public $__table = 'consumer'; // table name
|
||||
public $consumer_key; // varchar(255) primary_key not_null
|
||||
public $consumer_secret; // varchar(255) not_null
|
||||
public $consumer_key; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space
|
||||
public $consumer_secret; // varchar(191) not_null not 255 because utf8mb4 takes more space
|
||||
public $seed; // char(32) not_null
|
||||
public $created; // datetime not_null
|
||||
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
|
||||
@ -24,8 +24,8 @@ class Consumer extends Managed_DataObject
|
||||
return array(
|
||||
'description' => 'OAuth consumer record',
|
||||
'fields' => array(
|
||||
'consumer_key' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'unique identifier, root URL'),
|
||||
'consumer_secret' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'secret value'),
|
||||
'consumer_key' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'unique identifier, root URL'),
|
||||
'consumer_secret' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'secret value'),
|
||||
'seed' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'seed for new tokens by this consumer'),
|
||||
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
|
@ -35,7 +35,7 @@ class Conversation extends Managed_DataObject
|
||||
{
|
||||
public $__table = 'conversation'; // table name
|
||||
public $id; // int(4) primary_key not_null
|
||||
public $uri; // varchar(255) unique_key
|
||||
public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime not_null
|
||||
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
@ -44,7 +44,7 @@ class Conversation extends Managed_DataObject
|
||||
return array(
|
||||
'fields' => array(
|
||||
'id' => array('type' => 'int', 'not null' => true, 'description' => 'should be set from root notice id (since 2014-03-01 commit)'),
|
||||
'uri' => array('type' => 'varchar', 'not null'=>true, 'length' => 255, 'description' => 'URI of the conversation'),
|
||||
'uri' => array('type' => 'varchar', 'not null'=>true, 'length' => 191, 'description' => 'URI of the conversation'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
),
|
||||
|
@ -34,7 +34,7 @@ class Deleted_notice extends Managed_DataObject
|
||||
public $__table = 'deleted_notice'; // table name
|
||||
public $id; // int(4) primary_key not_null
|
||||
public $profile_id; // int(4) not_null
|
||||
public $uri; // varchar(255) unique_key
|
||||
public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime() not_null
|
||||
public $deleted; // datetime() not_null
|
||||
|
||||
@ -47,7 +47,7 @@ class Deleted_notice extends Managed_DataObject
|
||||
'fields' => array(
|
||||
'id' => array('type' => 'int', 'not null' => true, 'description' => 'identity of notice'),
|
||||
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'author of the notice'),
|
||||
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universally unique identifier, usually a tag URI'),
|
||||
'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the notice record was created'),
|
||||
'deleted' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the notice record was created'),
|
||||
),
|
||||
|
219
classes/File.php
@ -26,29 +26,36 @@ class File extends Managed_DataObject
|
||||
{
|
||||
public $__table = 'file'; // table name
|
||||
public $id; // int(4) primary_key not_null
|
||||
public $url; // varchar(255) unique_key
|
||||
public $urlhash; // varchar(64) unique_key
|
||||
public $url; // text
|
||||
public $filehash; // varchar(64) indexed
|
||||
public $mimetype; // varchar(50)
|
||||
public $size; // int(4)
|
||||
public $title; // varchar(255)
|
||||
public $title; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $date; // int(4)
|
||||
public $protected; // int(4)
|
||||
public $filename; // varchar(255)
|
||||
public $filename; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $width; // int(4)
|
||||
public $height; // int(4)
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
const URLHASH_ALG = 'sha256';
|
||||
const FILEHASH_ALG = 'sha256';
|
||||
|
||||
public static function schemaDef()
|
||||
{
|
||||
return array(
|
||||
'fields' => array(
|
||||
'id' => array('type' => 'serial', 'not null' => true),
|
||||
'url' => array('type' => 'varchar', 'length' => 255, 'description' => 'destination URL after following redirections'),
|
||||
'urlhash' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'sha256 of destination URL (url field)'),
|
||||
'url' => array('type' => 'text', 'description' => 'destination URL after following possible redirections'),
|
||||
'filehash' => array('type' => 'varchar', 'length' => 64, 'not null' => false, 'description' => 'sha256 of the file contents, only for locally stored files of course'),
|
||||
'mimetype' => array('type' => 'varchar', 'length' => 50, 'description' => 'mime type of resource'),
|
||||
'size' => array('type' => 'int', 'description' => 'size of resource when available'),
|
||||
'title' => array('type' => 'varchar', 'length' => 255, 'description' => 'title of resource when available'),
|
||||
'title' => array('type' => 'varchar', 'length' => 191, 'description' => 'title of resource when available'),
|
||||
'date' => array('type' => 'int', 'description' => 'date of resource according to http query'),
|
||||
'protected' => array('type' => 'int', 'description' => 'true when URL is private (needs login)'),
|
||||
'filename' => array('type' => 'varchar', 'length' => 255, 'description' => 'if a local file, name of the file'),
|
||||
'filename' => array('type' => 'varchar', 'length' => 191, 'description' => 'if a local file, name of the file'),
|
||||
'width' => array('type' => 'int', 'description' => 'width in pixels, if it can be described as such and data is available'),
|
||||
'height' => array('type' => 'int', 'description' => 'height in pixels, if it can be described as such and data is available'),
|
||||
|
||||
@ -56,7 +63,10 @@ class File extends Managed_DataObject
|
||||
),
|
||||
'primary key' => array('id'),
|
||||
'unique keys' => array(
|
||||
'file_url_key' => array('url'),
|
||||
'file_urlhash_key' => array('urlhash'),
|
||||
),
|
||||
'indexes' => array(
|
||||
'file_filehash_idx' => array('filehash'),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -77,10 +87,11 @@ class File extends Managed_DataObject
|
||||
// I don't know why we have to keep doing this but I'm adding this last check to avoid
|
||||
// uniqueness bugs.
|
||||
|
||||
$file = File::getKV('url', $given_url);
|
||||
$file = File::getKV('urlhash', self::hashurl($given_url));
|
||||
|
||||
if (!$file instanceof File) {
|
||||
$file = new File;
|
||||
$file->urlhash = self::hashurl($given_url);
|
||||
$file->url = $given_url;
|
||||
if (!empty($redir_data['protected'])) $file->protected = $redir_data['protected'];
|
||||
if (!empty($redir_data['title'])) $file->title = $redir_data['title'];
|
||||
@ -122,16 +133,23 @@ class File extends Managed_DataObject
|
||||
throw new ServerException('No canonical URL from given URL to process');
|
||||
}
|
||||
|
||||
$file = File::getKV('url', $given_url);
|
||||
if (!$file instanceof File) {
|
||||
$file = null;
|
||||
|
||||
try {
|
||||
$file = File::getByUrl($given_url);
|
||||
} catch (NoResultException $e) {
|
||||
// First check if we have a lookup trace for this URL already
|
||||
$file_redir = File_redirection::getKV('url', $given_url);
|
||||
if ($file_redir instanceof File_redirection) {
|
||||
try {
|
||||
$file_redir = File_redirection::getByUrl($given_url);
|
||||
$file = File::getKV('id', $file_redir->file_id);
|
||||
if (!$file instanceof File) {
|
||||
// File did not exist, let's clean up the File_redirection entry
|
||||
$file_redir->delete();
|
||||
}
|
||||
} catch (NoResultException $e) {
|
||||
// We just wanted to doublecheck whether a File_thumbnail we might've had
|
||||
// actually referenced an existing File object.
|
||||
}
|
||||
}
|
||||
|
||||
// If we still don't have a File object, let's create one now!
|
||||
@ -151,8 +169,7 @@ class File extends Managed_DataObject
|
||||
throw new ServerException(sprintf(_("Cannot process URL '%s'"), $given_url));
|
||||
}
|
||||
|
||||
// TODO: max field length
|
||||
if ($redir_url === $given_url || strlen($redir_url) > 255 || !$followRedirects) {
|
||||
if ($redir_url === $given_url || !$followRedirects) {
|
||||
// Save the File object based on our lookup trace
|
||||
$file = File::saveNew($redir_data, $given_url);
|
||||
} else {
|
||||
@ -167,7 +184,6 @@ class File extends Managed_DataObject
|
||||
$file = self::processNew($redir_url, $notice_id, /*followRedirects*/false);
|
||||
File_redirection::saveNew($redir_data, $file->id, $given_url);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$file instanceof File) {
|
||||
// This should only happen if File::saveNew somehow did not return a File object,
|
||||
@ -237,12 +253,7 @@ class File extends Managed_DataObject
|
||||
|
||||
static function filename(Profile $profile, $origname, $mimetype)
|
||||
{
|
||||
try {
|
||||
$ext = common_supported_mime_to_ext($mimetype);
|
||||
} catch (Exception $e) {
|
||||
// We don't support this mimetype, but let's guess the extension
|
||||
$ext = substr(strrchr($mimetype, '/'), 1);
|
||||
}
|
||||
$ext = self::guessMimeExtension($mimetype);
|
||||
|
||||
// Normalize and make the original filename more URL friendly.
|
||||
$origname = basename($origname, ".$ext");
|
||||
@ -263,6 +274,17 @@ class File extends Managed_DataObject
|
||||
return $filename;
|
||||
}
|
||||
|
||||
static function guessMimeExtension($mimetype)
|
||||
{
|
||||
try {
|
||||
$ext = common_supported_mime_to_ext($mimetype);
|
||||
} catch (Exception $e) {
|
||||
// We don't support this mimetype, but let's guess the extension
|
||||
$ext = substr(strrchr($mimetype, '/'), 1);
|
||||
}
|
||||
return strtolower($ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation for as-saved base filenames
|
||||
*/
|
||||
@ -303,7 +325,7 @@ class File extends Managed_DataObject
|
||||
|
||||
}
|
||||
|
||||
if (StatusNet::useHTTPS()) {
|
||||
if (GNUsocial::useHTTPS()) {
|
||||
|
||||
$sslserver = common_config('attachments', 'sslserver');
|
||||
|
||||
@ -381,6 +403,10 @@ class File extends Managed_DataObject
|
||||
* @param $crop bool Crop to the max-values' aspect ratio
|
||||
*
|
||||
* @return File_thumbnail
|
||||
*
|
||||
* @throws UseFileAsThumbnailException if the file is considered an image itself and should be itself as thumbnail
|
||||
* @throws UnsupportedMediaException if, despite trying, we can't understand how to make a thumbnail for this format
|
||||
* @throws ServerException on various other errors
|
||||
*/
|
||||
public function getThumbnail($width=null, $height=null, $crop=false, $force_still=true)
|
||||
{
|
||||
@ -394,67 +420,16 @@ class File extends Managed_DataObject
|
||||
}
|
||||
}
|
||||
|
||||
if ($width === null) {
|
||||
$width = common_config('thumbnail', 'width');
|
||||
$height = common_config('thumbnail', 'height');
|
||||
$crop = common_config('thumbnail', 'crop');
|
||||
}
|
||||
|
||||
if ($height === null) {
|
||||
$height = $width;
|
||||
$crop = true;
|
||||
}
|
||||
|
||||
// Get proper aspect ratio width and height before lookup
|
||||
// We have to do it through an ImageFile object because of orientation etc.
|
||||
// Only other solution would've been to rotate + rewrite uploaded files.
|
||||
list($width, $height, $x, $y, $w, $h) =
|
||||
$image->scaleToFit($width, $height, $crop);
|
||||
|
||||
$params = array('file_id'=> $this->id,
|
||||
'width' => $width,
|
||||
'height' => $height);
|
||||
$thumb = File_thumbnail::pkeyGet($params);
|
||||
if ($thumb instanceof File_thumbnail) {
|
||||
return $thumb;
|
||||
}
|
||||
|
||||
// throws exception on failure to generate thumbnail
|
||||
$outname = "thumb-{$width}x{$height}-" . $image->filename;
|
||||
$outpath = self::path($outname);
|
||||
|
||||
// The boundary box for our resizing
|
||||
$box = array('width'=>$width, 'height'=>$height,
|
||||
'x'=>$x, 'y'=>$y,
|
||||
'w'=>$w, 'h'=>$h);
|
||||
|
||||
// Doublecheck that parameters are sane and integers.
|
||||
if ($box['width'] < 1 || $box['width'] > common_config('thumbnail', 'maxsize')
|
||||
|| $box['height'] < 1 || $box['height'] > common_config('thumbnail', 'maxsize')
|
||||
|| $box['w'] < 1 || $box['x'] >= $image->width
|
||||
|| $box['h'] < 1 || $box['y'] >= $image->height) {
|
||||
// Fail on bad width parameter. If this occurs, it's due to algorithm in ImageFile->scaleToFit
|
||||
common_debug("Boundary box parameters for resize of {$image->filepath} : ".var_export($box,true));
|
||||
throw new ServerException('Bad thumbnail size parameters.');
|
||||
}
|
||||
|
||||
common_debug(sprintf('Generating a thumbnail of File id==%u of size %ux%u', $this->id, $width, $height));
|
||||
// Perform resize and store into file
|
||||
$image->resizeTo($outpath, $box);
|
||||
|
||||
// Avoid deleting the original
|
||||
if ($image->getPath() != self::path($image->filename)) {
|
||||
$image->unlink();
|
||||
}
|
||||
return File_thumbnail::saveThumbnail($this->id,
|
||||
self::url($outname),
|
||||
$width, $height,
|
||||
$outname);
|
||||
return $image->getFileThumbnail($width, $height, $crop);
|
||||
}
|
||||
|
||||
public function getPath()
|
||||
{
|
||||
return self::path($this->filename);
|
||||
$filepath = self::path($this->filename);
|
||||
if (!file_exists($filepath)) {
|
||||
throw new FileNotFoundException($filepath);
|
||||
}
|
||||
return $filepath;
|
||||
}
|
||||
|
||||
public function getUrl()
|
||||
@ -462,7 +437,7 @@ class File extends Managed_DataObject
|
||||
if (!empty($this->filename)) {
|
||||
// A locally stored file, so let's generate a URL for our instance.
|
||||
$url = self::url($this->filename);
|
||||
if ($url != $this->url) {
|
||||
if (self::hashurl($url) !== $this->urlhash) {
|
||||
// For indexing purposes, in case we do a lookup on the 'url' field.
|
||||
// also we're fixing possible changes from http to https, or paths
|
||||
$this->updateUrl($url);
|
||||
@ -474,16 +449,40 @@ class File extends Managed_DataObject
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
static public function getByUrl($url)
|
||||
{
|
||||
$file = new File();
|
||||
$file->urlhash = self::hashurl($url);
|
||||
if (!$file->find(true)) {
|
||||
throw new NoResultException($file);
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $hashstr String of (preferrably lower case) hexadecimal characters, same as result of 'hash_file(...)'
|
||||
*/
|
||||
static public function getByHash($hashstr, $alg=File::FILEHASH_ALG)
|
||||
{
|
||||
$file = new File();
|
||||
$file->filehash = strtolower($hashstr);
|
||||
if (!$file->find(true)) {
|
||||
throw new NoResultException($file);
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
||||
public function updateUrl($url)
|
||||
{
|
||||
$file = File::getKV('url', $url);
|
||||
$file = File::getKV('urlhash', self::hashurl($url));
|
||||
if ($file instanceof File) {
|
||||
throw new ServerException('URL already exists in DB');
|
||||
}
|
||||
$sql = 'UPDATE %1$s SET url=%2$s WHERE url=%3$s;';
|
||||
$sql = 'UPDATE %1$s SET urlhash=%2$s, url=%3$s WHERE urlhash=%4$s;';
|
||||
$result = $this->query(sprintf($sql, $this->__table,
|
||||
$this->_quote((string)self::hashurl($url)),
|
||||
$this->_quote((string)$url),
|
||||
$this->_quote((string)$this->url)));
|
||||
$this->_quote((string)$this->urlhash)));
|
||||
if ($result === false) {
|
||||
common_log_db_error($this, 'UPDATE', __FILE__);
|
||||
throw new ServerException("Could not UPDATE {$this->__table}.url");
|
||||
@ -502,9 +501,9 @@ class File extends Managed_DataObject
|
||||
|
||||
function blowCache($last=false)
|
||||
{
|
||||
self::blow('file:notice-ids:%s', $this->url);
|
||||
self::blow('file:notice-ids:%s', $this->urlhash);
|
||||
if ($last) {
|
||||
self::blow('file:notice-ids:%s;last', $this->url);
|
||||
self::blow('file:notice-ids:%s;last', $this->urlhash);
|
||||
}
|
||||
self::blow('file:notice-count:%d', $this->id);
|
||||
}
|
||||
@ -582,4 +581,54 @@ class File extends Managed_DataObject
|
||||
|
||||
return $title ?: null;
|
||||
}
|
||||
|
||||
static public function hashurl($url)
|
||||
{
|
||||
if (empty($url)) {
|
||||
throw new Exception('No URL provided to hash algorithm.');
|
||||
}
|
||||
return hash(self::URLHASH_ALG, $url);
|
||||
}
|
||||
|
||||
static public function beforeSchemaUpdate()
|
||||
{
|
||||
$table = strtolower(get_called_class());
|
||||
$schema = Schema::get();
|
||||
$schemadef = $schema->getTableDef($table);
|
||||
|
||||
// 2015-02-19 We have to upgrade our table definitions to have the urlhash field populated
|
||||
if (isset($schemadef['fields']['urlhash']) && isset($schemadef['unique keys']['file_urlhash_key'])) {
|
||||
// We already have the urlhash field, so no need to migrate it.
|
||||
return;
|
||||
}
|
||||
echo "\nFound old $table table, upgrading it to contain 'urlhash' field...";
|
||||
// We have to create a urlhash that is _not_ the primary key,
|
||||
// transfer data and THEN run checkSchema
|
||||
$schemadef['fields']['urlhash'] = array (
|
||||
'type' => 'varchar',
|
||||
'length' => 64,
|
||||
'not null' => true,
|
||||
'description' => 'sha256 of destination URL (url field)',
|
||||
);
|
||||
$schemadef['fields']['url'] = array (
|
||||
'type' => 'text',
|
||||
'description' => 'destination URL after following possible redirections',
|
||||
);
|
||||
unset($schemadef['unique keys']);
|
||||
$schema->ensureTable($table, $schemadef);
|
||||
echo "DONE.\n";
|
||||
|
||||
$classname = ucfirst($table);
|
||||
$tablefix = new $classname;
|
||||
// urlhash is hash('sha256', $url) in the File table
|
||||
echo "Updating urlhash fields in $table table...";
|
||||
// Maybe very MySQL specific :(
|
||||
$tablefix->query(sprintf('UPDATE %1$s SET %2$s=%3$s;',
|
||||
$schema->quoteIdentifier($table),
|
||||
'urlhash',
|
||||
// The line below is "result of sha256 on column `url`"
|
||||
'SHA2(url, 256)'));
|
||||
echo "DONE.\n";
|
||||
echo "Resuming core schema upgrade...";
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,8 @@ class File_redirection extends Managed_DataObject
|
||||
/* the code below is auto generated do not remove the above tag */
|
||||
|
||||
public $__table = 'file_redirection'; // table name
|
||||
public $url; // varchar(255) primary_key not_null
|
||||
public $urlhash; // varchar(64) primary_key not_null
|
||||
public $url; // text
|
||||
public $file_id; // int(4)
|
||||
public $redirections; // int(4)
|
||||
public $httpcode; // int(4)
|
||||
@ -42,19 +43,30 @@ class File_redirection extends Managed_DataObject
|
||||
{
|
||||
return array(
|
||||
'fields' => array(
|
||||
'url' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'short URL (or any other kind of redirect) for file (id)'),
|
||||
'urlhash' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'sha256 hash of the URL'),
|
||||
'url' => array('type' => 'text', 'description' => 'short URL (or any other kind of redirect) for file (id)'),
|
||||
'file_id' => array('type' => 'int', 'description' => 'short URL for what URL/file'),
|
||||
'redirections' => array('type' => 'int', 'description' => 'redirect count'),
|
||||
'httpcode' => array('type' => 'int', 'description' => 'HTTP status code (20x, 30x, etc.)'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
),
|
||||
'primary key' => array('url'),
|
||||
'primary key' => array('urlhash'),
|
||||
'foreign keys' => array(
|
||||
'file_redirection_file_id_fkey' => array('file' => array('file_id' => 'id')),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static public function getByUrl($url)
|
||||
{
|
||||
$file = new File_redirection();
|
||||
$file->urlhash = File::hashurl($url);
|
||||
if (!$file->find(true)) {
|
||||
throw new NoResultException($file);
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
||||
static function _commonHttp($url, $redirs) {
|
||||
$request = new HTTPClient($url);
|
||||
$request->setConfig(array(
|
||||
@ -161,17 +173,18 @@ class File_redirection extends Managed_DataObject
|
||||
*/
|
||||
public function where($in_url, $discover=true) {
|
||||
// let's see if we know this...
|
||||
$a = File::getKV('url', $in_url);
|
||||
|
||||
if (!empty($a)) {
|
||||
try {
|
||||
$a = File::getByUrl($in_url);
|
||||
// this is a direct link to $a->url
|
||||
return $a->url;
|
||||
} else {
|
||||
$b = File_redirection::getKV('url', $in_url);
|
||||
if (!empty($b)) {
|
||||
} catch (NoResultException $e) {
|
||||
try {
|
||||
$b = File_redirection::getByUrl($in_url);
|
||||
// this is a redirect to $b->file_id
|
||||
$a = File::getKV('id', $b->file_id);
|
||||
return $a->url;
|
||||
} catch (NoResultException $e) {
|
||||
// Oh well, let's keep going
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,6 +287,7 @@ class File_redirection extends Managed_DataObject
|
||||
$file_redir = File_redirection::getKV('url', $short_url);
|
||||
if (!$file_redir instanceof File_redirection) {
|
||||
$file_redir = new File_redirection;
|
||||
$file_redir->urlhash = File::hashurl($short_url);
|
||||
$file_redir->url = $short_url;
|
||||
$file_redir->file_id = $file_id;
|
||||
$file_redir->insert();
|
||||
@ -334,10 +348,53 @@ class File_redirection extends Managed_DataObject
|
||||
|
||||
function saveNew($data, $file_id, $url) {
|
||||
$file_redir = new File_redirection;
|
||||
$file_redir->urlhash = File::hashurl($short_url);
|
||||
$file_redir->url = $url;
|
||||
$file_redir->file_id = $file_id;
|
||||
$file_redir->redirections = intval($data['redirects']);
|
||||
$file_redir->httpcode = intval($data['code']);
|
||||
$file_redir->insert();
|
||||
}
|
||||
|
||||
static public function beforeSchemaUpdate()
|
||||
{
|
||||
$table = strtolower(get_called_class());
|
||||
$schema = Schema::get();
|
||||
$schemadef = $schema->getTableDef($table);
|
||||
|
||||
// 2015-02-19 We have to upgrade our table definitions to have the urlhash field populated
|
||||
if (isset($schemadef['fields']['urlhash']) && in_array('urlhash', $schemadef['primary key'])) {
|
||||
// We already have the urlhash field, so no need to migrate it.
|
||||
return;
|
||||
}
|
||||
echo "\nFound old $table table, upgrading it to contain 'urlhash' field...";
|
||||
// We have to create a urlhash that is _not_ the primary key,
|
||||
// transfer data and THEN run checkSchema
|
||||
$schemadef['fields']['urlhash'] = array (
|
||||
'type' => 'varchar',
|
||||
'length' => 64,
|
||||
'not null' => true,
|
||||
'description' => 'sha256 hash of the URL',
|
||||
);
|
||||
$schemadef['fields']['url'] = array (
|
||||
'type' => 'text',
|
||||
'description' => 'short URL (or any other kind of redirect) for file (id)',
|
||||
);
|
||||
unset($schemadef['primary key']);
|
||||
$schema->ensureTable($table, $schemadef);
|
||||
echo "DONE.\n";
|
||||
|
||||
$classname = ucfirst($table);
|
||||
$tablefix = new $classname;
|
||||
// urlhash is hash('sha256', $url) in the File table
|
||||
echo "Updating urlhash fields in $table table...";
|
||||
// Maybe very MySQL specific :(
|
||||
$tablefix->query(sprintf('UPDATE %1$s SET %2$s=%3$s;',
|
||||
$schema->quoteIdentifier($table),
|
||||
'urlhash',
|
||||
// The line below is "result of sha256 on column `url`"
|
||||
'SHA2(url, 256)'));
|
||||
echo "DONE.\n";
|
||||
echo "Resuming core schema upgrade...";
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +27,8 @@ class File_thumbnail extends Managed_DataObject
|
||||
{
|
||||
public $__table = 'file_thumbnail'; // table name
|
||||
public $file_id; // int(4) primary_key not_null
|
||||
public $url; // varchar(255) unique_key
|
||||
public $filename; // varchar(255)
|
||||
public $url; // text
|
||||
public $filename; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $width; // int(4) primary_key
|
||||
public $height; // int(4) primary_key
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
@ -38,8 +38,8 @@ class File_thumbnail extends Managed_DataObject
|
||||
return array(
|
||||
'fields' => array(
|
||||
'file_id' => array('type' => 'int', 'not null' => true, 'description' => 'thumbnail for what URL/file'),
|
||||
'url' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL of thumbnail'),
|
||||
'filename' => array('type' => 'varchar', 'length' => 255, 'description' => 'if stored locally, filename is put here'),
|
||||
'url' => array('type' => 'text', 'not null' => false, 'description' => 'URL of thumbnail'),
|
||||
'filename' => array('type' => 'varchar', 'length' => 191, 'description' => 'if stored locally, filename is put here'),
|
||||
'width' => array('type' => 'int', 'description' => 'width of thumbnail'),
|
||||
'height' => array('type' => 'int', 'description' => 'height of thumbnail'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
@ -117,46 +117,42 @@ class File_thumbnail extends Managed_DataObject
|
||||
return File::path($filename);
|
||||
}
|
||||
|
||||
static function url($filename)
|
||||
{
|
||||
// TODO: Store thumbnails in their own directory and don't use File::url here
|
||||
return File::url($filename);
|
||||
}
|
||||
|
||||
public function getPath()
|
||||
{
|
||||
return self::path($this->filename);
|
||||
$filepath = self::path($this->filename);
|
||||
if (!file_exists($filepath)) {
|
||||
throw new FileNotFoundException($filepath);
|
||||
}
|
||||
return $filepath;
|
||||
}
|
||||
|
||||
public function getUrl()
|
||||
{
|
||||
if (!empty($this->getFile()->filename)) {
|
||||
// A locally stored File, so let's generate a URL for our instance.
|
||||
$url = File::url($this->filename);
|
||||
if ($url != $this->url) {
|
||||
// For indexing purposes, in case we do a lookup on the 'url' field.
|
||||
// also we're fixing possible changes from http to https, or paths
|
||||
$this->updateUrl($url);
|
||||
// A locally stored File, so we can dynamically generate a URL.
|
||||
if (!empty($this->url)) {
|
||||
// Let's just clear this field as there is no point in having it for local files.
|
||||
$orig = clone($this);
|
||||
$this->url = null;
|
||||
$this->update($orig);
|
||||
}
|
||||
return $url;
|
||||
$url = common_local_url('attachment_thumbnail', array('attachment'=>$this->file_id));
|
||||
if (strpos($url, '?') === false) {
|
||||
$url .= '?';
|
||||
}
|
||||
return $url . http_build_query(array('w'=>$this->width, 'h'=>$this->height));
|
||||
}
|
||||
|
||||
// No local filename available, return the URL we have stored
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function updateUrl($url)
|
||||
{
|
||||
$file = File_thumbnail::getKV('url', $url);
|
||||
if ($file instanceof File_thumbnail) {
|
||||
throw new ServerException('URL already exists in DB');
|
||||
}
|
||||
$sql = 'UPDATE %1$s SET url=%2$s WHERE url=%3$s;';
|
||||
$result = $this->query(sprintf($sql, $this->__table,
|
||||
$this->_quote((string)$url),
|
||||
$this->_quote((string)$this->url)));
|
||||
if ($result === false) {
|
||||
common_log_db_error($this, 'UPDATE', __FILE__);
|
||||
throw new ServerException("Could not UPDATE {$this->__table}.url");
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function delete($useWhere=false)
|
||||
{
|
||||
if (!empty($this->filename) && file_exists(File_thumbnail::path($this->filename))) {
|
||||
|
@ -13,7 +13,7 @@ class Foreign_link extends Managed_DataObject
|
||||
public $user_id; // int(4) primary_key not_null
|
||||
public $foreign_id; // bigint(8) primary_key not_null unsigned
|
||||
public $service; // int(4) primary_key not_null
|
||||
public $credentials; // varchar(255)
|
||||
public $credentials; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $noticesync; // tinyint(1) not_null default_1
|
||||
public $friendsync; // tinyint(1) not_null default_2
|
||||
public $profilesync; // tinyint(1) not_null default_1
|
||||
@ -32,7 +32,7 @@ class Foreign_link extends Managed_DataObject
|
||||
'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'link to user on this system, if exists'),
|
||||
'foreign_id' => array('type' => 'int', 'size' => 'big', 'unsigned' => true, 'not null' => true, 'description' => 'link to user on foreign service, if exists'),
|
||||
'service' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to service'),
|
||||
'credentials' => array('type' => 'varchar', 'length' => 255, 'description' => 'authc credentials, typically a password'),
|
||||
'credentials' => array('type' => 'varchar', 'length' => 191, 'description' => 'authc credentials, typically a password'),
|
||||
'noticesync' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 1, 'description' => 'notice synchronization, bit 1 = sync outgoing, bit 2 = sync incoming, bit 3 = filter local replies'),
|
||||
'friendsync' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 2, 'description' => 'friend synchronization, bit 1 = sync outgoing, bit 2 = sync incoming'),
|
||||
'profilesync' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 1, 'description' => 'profile synchronization, bit 1 = sync outgoing, bit 2 = sync incoming'),
|
||||
|
@ -12,7 +12,7 @@ class Foreign_service extends Managed_DataObject
|
||||
public $__table = 'foreign_service'; // table name
|
||||
public $id; // int(4) primary_key not_null
|
||||
public $name; // varchar(32) unique_key not_null
|
||||
public $description; // varchar(255)
|
||||
public $description; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime() not_null
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
@ -25,7 +25,7 @@ class Foreign_service extends Managed_DataObject
|
||||
'fields' => array(
|
||||
'id' => array('type' => 'int', 'not null' => true, 'description' => 'numeric key for service'),
|
||||
'name' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'name of the service'),
|
||||
'description' => array('type' => 'varchar', 'length' => 255, 'description' => 'description'),
|
||||
'description' => array('type' => 'varchar', 'length' => 191, 'description' => 'description'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
),
|
||||
|
@ -12,8 +12,8 @@ class Foreign_user extends Managed_DataObject
|
||||
public $__table = 'foreign_user'; // table name
|
||||
public $id; // bigint(8) primary_key not_null
|
||||
public $service; // int(4) primary_key not_null
|
||||
public $uri; // varchar(255) unique_key not_null
|
||||
public $nickname; // varchar(255)
|
||||
public $uri; // varchar(191) unique_key not_null not 255 because utf8mb4 takes more space
|
||||
public $nickname; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime() not_null
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
@ -26,8 +26,8 @@ class Foreign_user extends Managed_DataObject
|
||||
'fields' => array(
|
||||
'id' => array('type' => 'int', 'size' => 'big', 'not null' => true, 'description' => 'unique numeric key on foreign service'),
|
||||
'service' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to service'),
|
||||
'uri' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'identifying URI'),
|
||||
'nickname' => array('type' => 'varchar', 'length' => 255, 'description' => 'nickname on foreign service'),
|
||||
'uri' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'identifying URI'),
|
||||
'nickname' => array('type' => 'varchar', 'length' => 191, 'description' => 'nickname on foreign service'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
),
|
||||
|
@ -12,7 +12,7 @@ class Group_member extends Managed_DataObject
|
||||
public $group_id; // int(4) primary_key not_null
|
||||
public $profile_id; // int(4) primary_key not_null
|
||||
public $is_admin; // tinyint(1)
|
||||
public $uri; // varchar(255)
|
||||
public $uri; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime() not_null
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
@ -26,7 +26,7 @@ class Group_member extends Managed_DataObject
|
||||
'group_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to user_group'),
|
||||
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'),
|
||||
'is_admin' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'is this user an admin?'),
|
||||
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universal identifier'),
|
||||
'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
),
|
||||
|
@ -12,7 +12,7 @@ class Invitation extends Managed_DataObject
|
||||
public $__table = 'invitation'; // table name
|
||||
public $code; // varchar(32) primary_key not_null
|
||||
public $user_id; // int(4) not_null
|
||||
public $address; // varchar(255) multiple_key not_null
|
||||
public $address; // varchar(191) multiple_key not_null not 255 because utf8mb4 takes more space
|
||||
public $address_type; // varchar(8) multiple_key not_null
|
||||
public $registered_user_id; // int(4) not_null
|
||||
public $created; // datetime() not_null
|
||||
@ -34,7 +34,7 @@ class Invitation extends Managed_DataObject
|
||||
'fields' => array(
|
||||
'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'random code for an invitation'),
|
||||
'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'who sent the invitation'),
|
||||
'address' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'invitation sent to'),
|
||||
'address' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'invitation sent to'),
|
||||
'address_type' => array('type' => 'varchar', 'length' => 8, 'not null' => true, 'description' => 'address type ("email", "xmpp", "sms")'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'registered_user_id' => array('type' => 'int', 'not null' => false, 'description' => 'if the invitation is converted, who the new user is'),
|
||||
|
@ -32,7 +32,7 @@ class Location_namespace extends Managed_DataObject
|
||||
|
||||
public $__table = 'location_namespace'; // table name
|
||||
public $id; // int(4) primary_key not_null
|
||||
public $description; // varchar(255)
|
||||
public $description; // varchar(191)
|
||||
public $created; // datetime() not_null
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
@ -44,7 +44,7 @@ class Location_namespace extends Managed_DataObject
|
||||
return array(
|
||||
'fields' => array(
|
||||
'id' => array('type' => 'int', 'not null' => true, 'description' => 'identity for this namespace'),
|
||||
'description' => array('type' => 'varchar', 'length' => 255, 'description' => 'description of the namespace'),
|
||||
'description' => array('type' => 'varchar', 'length' => 191, 'description' => 'description of the namespace'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date the record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
),
|
||||
|
@ -299,6 +299,11 @@ abstract class Managed_DataObject extends Memcached_DataObject
|
||||
return $ckeys;
|
||||
}
|
||||
|
||||
public function escapedTableName()
|
||||
{
|
||||
return common_database_tablename($this->tableName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ID, checked that it is set and reasonably valid
|
||||
*
|
||||
@ -391,4 +396,9 @@ abstract class Managed_DataObject extends Memcached_DataObject
|
||||
// @FIXME return true only if something changed (otherwise 0)
|
||||
return $result;
|
||||
}
|
||||
|
||||
static public function beforeSchemaUpdate()
|
||||
{
|
||||
// NOOP
|
||||
}
|
||||
}
|
||||
|
@ -734,7 +734,7 @@ class Memcached_DataObject extends Safe_DataObject
|
||||
return $string;
|
||||
}
|
||||
|
||||
// We overload so that 'SET NAMES "utf8"' is called for
|
||||
// We overload so that 'SET NAMES "utf8mb4"' is called for
|
||||
// each connection
|
||||
|
||||
function _connect()
|
||||
@ -784,9 +784,9 @@ class Memcached_DataObject extends Safe_DataObject
|
||||
$conn = $DB->connection;
|
||||
if (!empty($conn)) {
|
||||
if ($DB instanceof DB_mysqli || $DB instanceof MDB2_Driver_mysqli) {
|
||||
mysqli_set_charset($conn, 'utf8');
|
||||
mysqli_set_charset($conn, 'utf8mb4');
|
||||
} else if ($DB instanceof DB_mysql || $DB instanceof MDB2_Driver_mysql) {
|
||||
mysql_set_charset('utf8', $conn);
|
||||
mysql_set_charset('utf8mb4', $conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ class Nonce extends Managed_DataObject
|
||||
/* the code below is auto generated do not remove the above tag */
|
||||
|
||||
public $__table = 'nonce'; // table name
|
||||
public $consumer_key; // varchar(255) primary_key not_null
|
||||
public $consumer_key; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space
|
||||
public $tok; // char(32)
|
||||
public $nonce; // char(32) primary_key not_null
|
||||
public $ts; // datetime() primary_key not_null
|
||||
@ -39,7 +39,7 @@ class Nonce extends Managed_DataObject
|
||||
return array(
|
||||
'description' => 'OAuth nonce record',
|
||||
'fields' => array(
|
||||
'consumer_key' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'unique identifier, root URL'),
|
||||
'consumer_key' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'unique identifier, root URL'),
|
||||
'tok' => array('type' => 'char', 'length' => 32, 'description' => 'buggy old value, ignored'),
|
||||
'nonce' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'nonce'),
|
||||
'ts' => array('type' => 'datetime', 'not null' => true, 'description' => 'timestamp sent'),
|
||||
|
@ -55,10 +55,10 @@ class Notice extends Managed_DataObject
|
||||
public $__table = 'notice'; // table name
|
||||
public $id; // int(4) primary_key not_null
|
||||
public $profile_id; // int(4) multiple_key not_null
|
||||
public $uri; // varchar(255) unique_key
|
||||
public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
|
||||
public $content; // text
|
||||
public $rendered; // text
|
||||
public $url; // varchar(255)
|
||||
public $url; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime multiple_key not_null default_0000-00-00%2000%3A00%3A00
|
||||
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
|
||||
public $reply_to; // int(4)
|
||||
@ -70,8 +70,8 @@ class Notice extends Managed_DataObject
|
||||
public $location_id; // int(4)
|
||||
public $location_ns; // int(4)
|
||||
public $repeat_of; // int(4)
|
||||
public $verb; // varchar(255)
|
||||
public $object_type; // varchar(255)
|
||||
public $verb; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $object_type; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $scope; // int(4)
|
||||
|
||||
/* the code above is auto generated do not remove the tag below */
|
||||
@ -83,10 +83,10 @@ class Notice extends Managed_DataObject
|
||||
'fields' => array(
|
||||
'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
|
||||
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'who made the update'),
|
||||
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universally unique identifier, usually a tag URI'),
|
||||
'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'),
|
||||
'content' => array('type' => 'text', 'description' => 'update content', 'collate' => 'utf8_general_ci'),
|
||||
'rendered' => array('type' => 'text', 'description' => 'HTML version of the content'),
|
||||
'url' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL of any attachment (image, video, bookmark, whatever)'),
|
||||
'url' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL of any attachment (image, video, bookmark, whatever)'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
'reply_to' => array('type' => 'int', 'description' => 'notice replied to (usually a guess)'),
|
||||
@ -98,8 +98,8 @@ class Notice extends Managed_DataObject
|
||||
'location_id' => array('type' => 'int', 'description' => 'location id if possible'),
|
||||
'location_ns' => array('type' => 'int', 'description' => 'namespace for location'),
|
||||
'repeat_of' => array('type' => 'int', 'description' => 'notice this is a repeat of'),
|
||||
'object_type' => array('type' => 'varchar', 'length' => 255, 'description' => 'URI representing activity streams object type', 'default' => 'http://activitystrea.ms/schema/1.0/note'),
|
||||
'verb' => array('type' => 'varchar', 'length' => 255, 'description' => 'URI representing activity streams verb', 'default' => 'http://activitystrea.ms/schema/1.0/post'),
|
||||
'object_type' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams object type', 'default' => 'http://activitystrea.ms/schema/1.0/note'),
|
||||
'verb' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams verb', 'default' => 'http://activitystrea.ms/schema/1.0/post'),
|
||||
'scope' => array('type' => 'int',
|
||||
'description' => 'bit map for distribution scope; 0 = everywhere; 1 = this server only; 2 = addressees; 4 = followers; null = default'),
|
||||
),
|
||||
@ -313,6 +313,16 @@ class Notice extends Managed_DataObject
|
||||
return $notice;
|
||||
}
|
||||
|
||||
public static function getById($id)
|
||||
{
|
||||
$notice = new Notice();
|
||||
$notice->id = $id;
|
||||
if (!$notice->find(true)) {
|
||||
throw new NoResultException($notice);
|
||||
}
|
||||
return $notice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract #hashtags from this notice's content and save them to the database.
|
||||
*/
|
||||
@ -892,6 +902,12 @@ class Notice extends Managed_DataObject
|
||||
$stored->insert(); // throws exception on error
|
||||
$orig = clone($stored); // for updating later in this try clause
|
||||
|
||||
$object = null;
|
||||
Event::handle('StoreActivityObject', array($act, $stored, $options, &$object));
|
||||
if (empty($object)) {
|
||||
throw new ServerException('Unsuccessful call to StoreActivityObject '.$stored->uri . ': '.$act->asString());
|
||||
}
|
||||
|
||||
// If it's not part of a conversation, it's
|
||||
// the beginning of a new conversation.
|
||||
if (empty($stored->conversation)) {
|
||||
@ -900,12 +916,6 @@ class Notice extends Managed_DataObject
|
||||
$stored->conversation = $conv->id;
|
||||
}
|
||||
|
||||
$object = null;
|
||||
Event::handle('StoreActivityObject', array($act, $stored, $options, &$object));
|
||||
if (empty($object)) {
|
||||
throw new ServerException('No object from StoreActivityObject '.$stored->uri . ': '.$act->asString());
|
||||
}
|
||||
$stored->object_type = ActivityUtils::resolveUri($object->getObjectType(), true);
|
||||
$stored->update($orig);
|
||||
} catch (Exception $e) {
|
||||
if (empty($stored->id)) {
|
||||
@ -1067,13 +1077,9 @@ class Notice extends Managed_DataObject
|
||||
}
|
||||
|
||||
$args = func_get_args();
|
||||
|
||||
$format = array_shift($args);
|
||||
|
||||
$keyPart = vsprintf($format, $args);
|
||||
|
||||
$cacheKey = Cache::key($keyPart);
|
||||
|
||||
$c->delete($cacheKey);
|
||||
|
||||
// delete the "last" stream, too, if this notice is
|
||||
@ -1194,17 +1200,13 @@ class Notice extends Managed_DataObject
|
||||
}
|
||||
|
||||
$f2ps = File_to_post::listGet('post_id', array($this->id));
|
||||
|
||||
$ids = array();
|
||||
|
||||
foreach ($f2ps[$this->id] as $f2p) {
|
||||
$ids[] = $f2p->file_id;
|
||||
}
|
||||
|
||||
$files = File::multiGet('id', $ids);
|
||||
|
||||
$this->_attachments[$this->id] = $files->fetchAll();
|
||||
|
||||
return $this->_attachments[$this->id];
|
||||
}
|
||||
|
||||
@ -1766,15 +1768,12 @@ class Notice extends Managed_DataObject
|
||||
|
||||
$ids = array();
|
||||
|
||||
foreach ($gis[$this->id] as $gi)
|
||||
{
|
||||
foreach ($gis[$this->id] as $gi) {
|
||||
$ids[] = $gi->group_id;
|
||||
}
|
||||
|
||||
$groups = User_group::multiGet('id', $ids);
|
||||
|
||||
$this->_groups[$this->id] = $groups->fetchAll();
|
||||
|
||||
return $this->_groups[$this->id];
|
||||
}
|
||||
|
||||
@ -1819,17 +1818,7 @@ class Notice extends Managed_DataObject
|
||||
|
||||
$act->verb = $this->verb;
|
||||
|
||||
if ($this->repeat_of) {
|
||||
$repeated = Notice::getKV('id', $this->repeat_of);
|
||||
if ($repeated instanceof Notice) {
|
||||
// TRANS: A repeat activity's title. %1$s is repeater's nickname
|
||||
// and %2$s is the repeated user's nickname.
|
||||
$act->title = sprintf(_('%1$s repeated a notice by %2$s'),
|
||||
$this->getProfile()->getNickname(),
|
||||
$repeated->getProfile()->getNickname());
|
||||
$act->objects[] = $repeated->asActivity($scoped);
|
||||
}
|
||||
} else {
|
||||
if (!$this->repeat_of) {
|
||||
$act->objects[] = $this->asActivityObject();
|
||||
}
|
||||
|
||||
@ -2687,34 +2676,26 @@ class Notice extends Managed_DataObject
|
||||
$scope = self::defaultScope();
|
||||
}
|
||||
|
||||
// If there's no scope, anyone (even anon) is in scope.
|
||||
|
||||
if ($scope == 0) { // Not private
|
||||
|
||||
if ($scope == 0 && !$this->getProfile()->isPrivateStream()) { // Not scoping, so it is public.
|
||||
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,
|
||||
@ -2726,7 +2707,6 @@ class Notice extends Managed_DataObject
|
||||
}
|
||||
|
||||
// Only for members of the given group
|
||||
|
||||
if ($scope & Notice::GROUP_SCOPE) {
|
||||
|
||||
// XXX: just query for the single membership
|
||||
@ -2747,26 +2727,15 @@ class Notice extends Managed_DataObject
|
||||
}
|
||||
}
|
||||
|
||||
// Only for followers of the author
|
||||
if ($scope & Notice::FOLLOWER_SCOPE || $this->getProfile()->isPrivateStream()) {
|
||||
|
||||
$author = null;
|
||||
|
||||
if ($scope & Notice::FOLLOWER_SCOPE) {
|
||||
|
||||
try {
|
||||
$author = $this->getProfile();
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Subscription::exists($profile, $author)) {
|
||||
if (!Subscription::exists($profile, $this->getProfile())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !$this->isHiddenSpam($profile);
|
||||
}
|
||||
}
|
||||
|
||||
function isHiddenSpam($profile) {
|
||||
|
||||
@ -2836,7 +2805,6 @@ class Notice extends Managed_DataObject
|
||||
static function fillProfiles($notices)
|
||||
{
|
||||
$map = self::getProfiles($notices);
|
||||
|
||||
foreach ($notices as $entry=>$notice) {
|
||||
try {
|
||||
if (array_key_exists($notice->profile_id, $map)) {
|
||||
@ -2857,22 +2825,17 @@ class Notice extends Managed_DataObject
|
||||
foreach ($notices as $notice) {
|
||||
$ids[] = $notice->profile_id;
|
||||
}
|
||||
|
||||
$ids = array_unique($ids);
|
||||
|
||||
return Profile::pivotGet('id', $ids);
|
||||
}
|
||||
|
||||
static function fillGroups(&$notices)
|
||||
{
|
||||
$ids = self::_idsOf($notices);
|
||||
|
||||
$gis = Group_inbox::listGet('notice_id', $ids);
|
||||
|
||||
$gids = array();
|
||||
|
||||
foreach ($gis as $id => $gi)
|
||||
{
|
||||
foreach ($gis as $id => $gi) {
|
||||
foreach ($gi as $g)
|
||||
{
|
||||
$gids[] = $g->group_id;
|
||||
@ -2880,9 +2843,7 @@ class Notice extends Managed_DataObject
|
||||
}
|
||||
|
||||
$gids = array_unique($gids);
|
||||
|
||||
$group = User_group::pivotGet('id', $gids);
|
||||
|
||||
foreach ($notices as $notice)
|
||||
{
|
||||
$grps = array();
|
||||
@ -2906,11 +2867,8 @@ class Notice extends Managed_DataObject
|
||||
static function fillAttachments(&$notices)
|
||||
{
|
||||
$ids = self::_idsOf($notices);
|
||||
|
||||
$f2pMap = File_to_post::listGet('post_id', $ids);
|
||||
|
||||
$fileIds = array();
|
||||
|
||||
foreach ($f2pMap as $noticeId => $f2ps) {
|
||||
foreach ($f2ps as $f2p) {
|
||||
$fileIds[] = $f2p->file_id;
|
||||
@ -2918,9 +2876,7 @@ class Notice extends Managed_DataObject
|
||||
}
|
||||
|
||||
$fileIds = array_unique($fileIds);
|
||||
|
||||
$fileMap = File::pivotGet('id', $fileIds);
|
||||
|
||||
foreach ($notices as $notice)
|
||||
{
|
||||
$files = array();
|
||||
@ -2945,31 +2901,4 @@ class Notice extends Managed_DataObject
|
||||
$notice->_setReplies($ids);
|
||||
}
|
||||
}
|
||||
|
||||
protected $_repeats = array();
|
||||
|
||||
function getRepeats()
|
||||
{
|
||||
if (isset($this->_repeats[$this->id])) {
|
||||
return $this->_repeats[$this->id];
|
||||
}
|
||||
$repeatMap = Notice::listGet('repeat_of', array($this->id));
|
||||
$this->_repeats[$this->id] = $repeatMap[$this->id];
|
||||
return $this->_repeats[$this->id];
|
||||
}
|
||||
|
||||
function _setRepeats($repeats)
|
||||
{
|
||||
$this->_repeats[$this->id] = $repeats;
|
||||
}
|
||||
|
||||
static function fillRepeats(&$notices)
|
||||
{
|
||||
$ids = self::_idsOf($notices);
|
||||
$repeatMap = Notice::listGet('repeat_of', $ids);
|
||||
foreach ($notices as $notice) {
|
||||
$repeats = $repeatMap[$notice->id];
|
||||
$notice->_setRepeats($repeats);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,8 @@ class Notice_source extends Managed_DataObject
|
||||
|
||||
public $__table = 'notice_source'; // table name
|
||||
public $code; // varchar(32) primary_key not_null
|
||||
public $name; // varchar(255) not_null
|
||||
public $url; // varchar(255) not_null
|
||||
public $name; // varchar(191) not_null not 255 because utf8mb4 takes more space
|
||||
public $url; // varchar(191) not_null not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime() not_null
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
@ -24,8 +24,8 @@ class Notice_source extends Managed_DataObject
|
||||
return array(
|
||||
'fields' => array(
|
||||
'code' => array('type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'source code'),
|
||||
'name' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'name of the source'),
|
||||
'url' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'url to link to'),
|
||||
'name' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'name of the source'),
|
||||
'url' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'url to link to'),
|
||||
'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
|
@ -12,14 +12,14 @@ class Oauth_application extends Managed_DataObject
|
||||
public $__table = 'oauth_application'; // table name
|
||||
public $id; // int(4) primary_key not_null
|
||||
public $owner; // int(4) not_null
|
||||
public $consumer_key; // varchar(255) not_null
|
||||
public $name; // varchar(255) not_null
|
||||
public $description; // varchar(255)
|
||||
public $icon; // varchar(255) not_null
|
||||
public $source_url; // varchar(255)
|
||||
public $organization; // varchar(255)
|
||||
public $homepage; // varchar(255)
|
||||
public $callback_url; // varchar(255) not_null
|
||||
public $consumer_key; // varchar(191) not_null not 255 because utf8mb4 takes more space
|
||||
public $name; // varchar(191) not_null not 255 because utf8mb4 takes more space
|
||||
public $description; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $icon; // varchar(191) not_null not 255 because utf8mb4 takes more space
|
||||
public $source_url; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $organization; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $homepage; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $callback_url; // varchar(191) not_null not 255 because utf8mb4 takes more space
|
||||
public $type; // tinyint(1)
|
||||
public $access_type; // tinyint(1)
|
||||
public $created; // datetime not_null
|
||||
@ -43,12 +43,12 @@ class Oauth_application extends Managed_DataObject
|
||||
static function maxDesc()
|
||||
{
|
||||
// This used to default to textlimit or allow unlimited descriptions,
|
||||
// but this isn't part of a notice and the field's limited to 255 chars
|
||||
// in the DB, so those seem silly.
|
||||
// but this isn't part of a notice and the field's limited to 191 chars
|
||||
// in the DB, so those seem silly. (utf8mb4 takes up more space, so can't use 255)
|
||||
//
|
||||
// Now just defaulting to 255 max unless a smaller application desclimit
|
||||
// Now just defaulting to 191 max unless a smaller application desclimit
|
||||
// is actually set. Setting to 0 will use the maximum.
|
||||
$max = 255;
|
||||
$max = 191;
|
||||
$desclimit = intval(common_config('application', 'desclimit'));
|
||||
if ($desclimit > 0 && $desclimit < $max) {
|
||||
return $desclimit;
|
||||
@ -80,7 +80,7 @@ class Oauth_application extends Managed_DataObject
|
||||
|
||||
function setOriginal($filename)
|
||||
{
|
||||
$imagefile = new ImageFile($this->id, Avatar::path($filename));
|
||||
$imagefile = new ImageFile(null, Avatar::path($filename));
|
||||
|
||||
// XXX: Do we want to have a bunch of different size icons? homepage, stream, mini?
|
||||
// or just one and control size via CSS? --Zach
|
||||
@ -163,14 +163,14 @@ class Oauth_application extends Managed_DataObject
|
||||
'fields' => array(
|
||||
'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
|
||||
'owner' => array('type' => 'int', 'not null' => true, 'description' => 'owner of the application'),
|
||||
'consumer_key' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'application consumer key'),
|
||||
'name' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'name of the application'),
|
||||
'description' => array('type' => 'varchar', 'length' => 255, 'description' => 'description of the application'),
|
||||
'icon' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'application icon'),
|
||||
'source_url' => array('type' => 'varchar', 'length' => 255, 'description' => 'application homepage - used for source link'),
|
||||
'organization' => array('type' => 'varchar', 'length' => 255, 'description' => 'name of the organization running the application'),
|
||||
'homepage' => array('type' => 'varchar', 'length' => 255, 'description' => 'homepage for the organization'),
|
||||
'callback_url' => array('type' => 'varchar', 'length' => 255, 'description' => 'url to redirect to after authentication'),
|
||||
'consumer_key' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'application consumer key'),
|
||||
'name' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'name of the application'),
|
||||
'description' => array('type' => 'varchar', 'length' => 191, 'description' => 'description of the application'),
|
||||
'icon' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'application icon'),
|
||||
'source_url' => array('type' => 'varchar', 'length' => 191, 'description' => 'application homepage - used for source link'),
|
||||
'organization' => array('type' => 'varchar', 'length' => 191, 'description' => 'name of the organization running the application'),
|
||||
'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'homepage for the organization'),
|
||||
'callback_url' => array('type' => 'varchar', 'length' => 191, 'description' => 'url to redirect to after authentication'),
|
||||
'type' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'type of app, 1 = browser, 2 = desktop'),
|
||||
'access_type' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'default access type, bit 1 = read, bit 2 = write'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
|
@ -13,7 +13,7 @@ class Oauth_application_user extends Managed_DataObject
|
||||
public $profile_id; // int(4) primary_key not_null
|
||||
public $application_id; // int(4) primary_key not_null
|
||||
public $access_type; // tinyint(1)
|
||||
public $token; // varchar(255)
|
||||
public $token; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime not_null
|
||||
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
@ -27,7 +27,7 @@ class Oauth_application_user extends Managed_DataObject
|
||||
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'user of the application'),
|
||||
'application_id' => array('type' => 'int', 'not null' => true, 'description' => 'id of the application'),
|
||||
'access_type' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'access type, bit 1 = read, bit 2 = write'),
|
||||
'token' => array('type' => 'varchar', 'length' => 255, 'description' => 'request or access token'),
|
||||
'token' => array('type' => 'varchar', 'length' => 191, 'description' => 'request or access token'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
),
|
||||
|
@ -12,7 +12,7 @@ class Oauth_token_association extends Managed_DataObject
|
||||
public $__table = 'oauth_token_association'; // table name
|
||||
public $profile_id; // int(4) primary_key not_null
|
||||
public $application_id; // int(4) primary_key not_null
|
||||
public $token; // varchar(255) primary key not null
|
||||
public $token; // varchar(191) primary key not null not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime not_null
|
||||
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
@ -43,7 +43,7 @@ class Oauth_token_association extends Managed_DataObject
|
||||
'fields' => array(
|
||||
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'associated user'),
|
||||
'application_id' => array('type' => 'int', 'not null' => true, 'description' => 'the application'),
|
||||
'token' => array('type' => 'varchar', 'length' => '255', 'not null' => true, 'description' => 'token used for this association'),
|
||||
'token' => array('type' => 'varchar', 'length' => '191', 'not null' => true, 'description' => 'token used for this association'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
),
|
||||
|
@ -30,11 +30,11 @@ class Profile extends Managed_DataObject
|
||||
public $__table = 'profile'; // table name
|
||||
public $id; // int(4) primary_key not_null
|
||||
public $nickname; // varchar(64) multiple_key not_null
|
||||
public $fullname; // varchar(255) multiple_key
|
||||
public $profileurl; // varchar(255)
|
||||
public $homepage; // varchar(255) multiple_key
|
||||
public $fullname; // varchar(191) multiple_key not 255 because utf8mb4 takes more space
|
||||
public $profileurl; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $homepage; // varchar(191) multiple_key not 255 because utf8mb4 takes more space
|
||||
public $bio; // text() multiple_key
|
||||
public $location; // varchar(255) multiple_key
|
||||
public $location; // varchar(191) multiple_key not 255 because utf8mb4 takes more space
|
||||
public $lat; // decimal(10,7)
|
||||
public $lon; // decimal(10,7)
|
||||
public $location_id; // int(4)
|
||||
@ -49,11 +49,11 @@ class Profile extends Managed_DataObject
|
||||
'fields' => array(
|
||||
'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
|
||||
'nickname' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'nickname or username', 'collate' => 'utf8_general_ci'),
|
||||
'fullname' => array('type' => 'varchar', 'length' => 255, 'description' => 'display name', 'collate' => 'utf8_general_ci'),
|
||||
'profileurl' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL, cached so we dont regenerate'),
|
||||
'homepage' => array('type' => 'varchar', 'length' => 255, 'description' => 'identifying URL', 'collate' => 'utf8_general_ci'),
|
||||
'fullname' => array('type' => 'varchar', 'length' => 191, 'description' => 'display name', 'collate' => 'utf8_general_ci'),
|
||||
'profileurl' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL, cached so we dont regenerate'),
|
||||
'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'identifying URL', 'collate' => 'utf8_general_ci'),
|
||||
'bio' => array('type' => 'text', 'description' => 'descriptive biography', 'collate' => 'utf8_general_ci'),
|
||||
'location' => array('type' => 'varchar', 'length' => 255, 'description' => 'physical location', 'collate' => 'utf8_general_ci'),
|
||||
'location' => array('type' => 'varchar', 'length' => 191, 'description' => 'physical location', 'collate' => 'utf8_general_ci'),
|
||||
'lat' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'),
|
||||
'lon' => array('type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'),
|
||||
'location_id' => array('type' => 'int', 'description' => 'location id if possible'),
|
||||
@ -160,7 +160,7 @@ class Profile extends Managed_DataObject
|
||||
return $this->getGroup()->setOriginal($filename);
|
||||
}
|
||||
|
||||
$imagefile = new ImageFile($this->id, Avatar::path($filename));
|
||||
$imagefile = new ImageFile(null, Avatar::path($filename));
|
||||
|
||||
$avatar = new Avatar();
|
||||
$avatar->profile_id = $this->id;
|
||||
@ -1451,6 +1451,12 @@ class Profile extends Managed_DataObject
|
||||
return $feed;
|
||||
}
|
||||
|
||||
public function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null)
|
||||
{
|
||||
// TRANS: Exception thrown when trying view "repeated to me".
|
||||
throw new Exception(_('Not implemented since inbox change.'));
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a Profile object by URI. Will call external plugins for help
|
||||
* using the event StartGetProfileFromURI.
|
||||
@ -1566,6 +1572,15 @@ class Profile extends Managed_DataObject
|
||||
return $this->getUser()->shortenLinks($text, $always);
|
||||
}
|
||||
|
||||
public function isPrivateStream()
|
||||
{
|
||||
// We only know of public remote users as of yet...
|
||||
if (!$this->isLocal()) {
|
||||
return false;
|
||||
}
|
||||
return $this->getUser()->private_stream ? true : false;
|
||||
}
|
||||
|
||||
public function delPref($namespace, $topic) {
|
||||
return Profile_prefs::setData($this, $namespace, $topic, null);
|
||||
}
|
||||
|
@ -43,8 +43,8 @@ class Profile_list extends Managed_DataObject
|
||||
public $private; // tinyint(1)
|
||||
public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
|
||||
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
|
||||
public $uri; // varchar(255) unique_key
|
||||
public $mainpage; // varchar(255)
|
||||
public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
|
||||
public $mainpage; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $tagged_count; // smallint
|
||||
public $subscriber_count; // smallint
|
||||
|
||||
@ -64,8 +64,8 @@ class Profile_list extends Managed_DataObject
|
||||
'created' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date the tag was added'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date the tag was modified'),
|
||||
|
||||
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universal identifier'),
|
||||
'mainpage' => array('type' => 'varchar', 'length' => 255, 'description' => 'page to link to'),
|
||||
'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'),
|
||||
'mainpage' => array('type' => 'varchar', 'length' => 191, 'description' => 'page to link to'),
|
||||
'tagged_count' => array('type' => 'int', 'default' => 0, 'description' => 'number of people tagged with this tag by this user'),
|
||||
'subscriber_count' => array('type' => 'int', 'default' => 0, 'description' => 'number of subscribers to this tag'),
|
||||
),
|
||||
|
@ -31,8 +31,8 @@ class Profile_prefs extends Managed_DataObject
|
||||
{
|
||||
public $__table = 'profile_prefs'; // table name
|
||||
public $profile_id; // int(4) primary_key not_null
|
||||
public $namespace; // varchar(255) not_null
|
||||
public $topic; // varchar(255) not_null
|
||||
public $namespace; // varchar(191) not_null
|
||||
public $topic; // varchar(191) not_null
|
||||
public $data; // text
|
||||
public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
|
||||
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
|
||||
@ -42,8 +42,8 @@ class Profile_prefs extends Managed_DataObject
|
||||
return array(
|
||||
'fields' => array(
|
||||
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'user'),
|
||||
'namespace' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'namespace, like pluginname or category'),
|
||||
'topic' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'preference key, i.e. description, age...'),
|
||||
'namespace' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'namespace, like pluginname or category'),
|
||||
'topic' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'preference key, i.e. description, age...'),
|
||||
'data' => array('type' => 'blob', 'description' => 'topic data, may be anything'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
|
@ -12,7 +12,7 @@ class Sms_carrier extends Managed_DataObject
|
||||
public $__table = 'sms_carrier'; // table name
|
||||
public $id; // int(4) primary_key not_null
|
||||
public $name; // varchar(64) unique_key
|
||||
public $email_pattern; // varchar(255) not_null
|
||||
public $email_pattern; // varchar(191) not_null not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime() not_null
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
@ -30,7 +30,7 @@ class Sms_carrier extends Managed_DataObject
|
||||
'fields' => array(
|
||||
'id' => array('type' => 'int', 'not null' => true, 'description' => 'primary key for SMS carrier'),
|
||||
'name' => array('type' => 'varchar', 'length' => 64, 'description' => 'name of the carrier'),
|
||||
'email_pattern' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'sprintf pattern for making an email address from a phone number'),
|
||||
'email_pattern' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'sprintf pattern for making an email address from a phone number'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
),
|
||||
|
@ -29,15 +29,15 @@ class Status_network extends Safe_DataObject
|
||||
public $__table = 'status_network'; // table name
|
||||
public $site_id; // int(4) primary_key not_null
|
||||
public $nickname; // varchar(64) unique_key not_null
|
||||
public $hostname; // varchar(255) unique_key
|
||||
public $pathname; // varchar(255) unique_key
|
||||
public $dbhost; // varchar(255)
|
||||
public $dbuser; // varchar(255)
|
||||
public $dbpass; // varchar(255)
|
||||
public $dbname; // varchar(255)
|
||||
public $sitename; // varchar(255)
|
||||
public $theme; // varchar(255)
|
||||
public $logo; // varchar(255)
|
||||
public $hostname; // varchar(191) unique_key not 255 because utf8mb4 takes more space
|
||||
public $pathname; // varchar(191) unique_key not 255 because utf8mb4 takes more space
|
||||
public $dbhost; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $dbuser; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $dbpass; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $dbname; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $sitename; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $theme; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $logo; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime() not_null
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
|
@ -32,9 +32,9 @@ class Subscription extends Managed_DataObject
|
||||
public $subscribed; // int(4) primary_key not_null
|
||||
public $jabber; // tinyint(1) default_1
|
||||
public $sms; // tinyint(1) default_1
|
||||
public $token; // varchar(255)
|
||||
public $secret; // varchar(255)
|
||||
public $uri; // varchar(255)
|
||||
public $token; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $secret; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $uri; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime() not_null
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
@ -46,9 +46,9 @@ class Subscription extends Managed_DataObject
|
||||
'subscribed' => array('type' => 'int', 'not null' => true, 'description' => 'profile being listened to'),
|
||||
'jabber' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'deliver jabber messages'),
|
||||
'sms' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'deliver sms messages'),
|
||||
'token' => array('type' => 'varchar', 'length' => 255, 'description' => 'authorization token'),
|
||||
'secret' => array('type' => 'varchar', 'length' => 255, 'description' => 'token secret'),
|
||||
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universally unique identifier'),
|
||||
'token' => array('type' => 'varchar', 'length' => 191, 'description' => 'authorization token'),
|
||||
'secret' => array('type' => 'varchar', 'length' => 191, 'description' => 'token secret'),
|
||||
'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
),
|
||||
@ -94,8 +94,12 @@ class Subscription extends Managed_DataObject
|
||||
if (Event::handle('StartSubscribe', array($subscriber, $other))) {
|
||||
$otherUser = User::getKV('id', $other->id);
|
||||
if ($otherUser instanceof User && $otherUser->subscribe_policy == User::SUBSCRIBE_POLICY_MODERATE && !$force) {
|
||||
try {
|
||||
$sub = Subscription_queue::saveNew($subscriber, $other);
|
||||
$sub->notify();
|
||||
} catch (AlreadyFulfilledException $e) {
|
||||
$sub = Subscription_queue::getSubQueue($subscriber, $other);
|
||||
}
|
||||
} else {
|
||||
$sub = self::saveNew($subscriber->id, $other->id);
|
||||
$sub->notify();
|
||||
@ -124,7 +128,7 @@ class Subscription extends Managed_DataObject
|
||||
}
|
||||
}
|
||||
|
||||
if ($sub instanceof Subscription) { // i.e. not SubscriptionQueue
|
||||
if ($sub instanceof Subscription) { // i.e. not Subscription_queue
|
||||
Event::handle('EndSubscribe', array($subscriber, $other));
|
||||
}
|
||||
}
|
||||
@ -132,6 +136,16 @@ class Subscription extends Managed_DataObject
|
||||
return $sub;
|
||||
}
|
||||
|
||||
static function ensureStart(Profile $subscriber, Profile $other, $force=false)
|
||||
{
|
||||
try {
|
||||
$sub = self::start($subscriber, $other, $force);
|
||||
} catch (AlreadyFulfilledException $e) {
|
||||
return self::getSubscription($subscriber, $other);
|
||||
}
|
||||
return $sub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Low-level subscription save.
|
||||
* Outside callers should use Subscription::start()
|
||||
@ -232,9 +246,25 @@ class Subscription extends Managed_DataObject
|
||||
|
||||
static function exists(Profile $subscriber, Profile $other)
|
||||
{
|
||||
$sub = Subscription::pkeyGet(array('subscriber' => $subscriber->id,
|
||||
'subscribed' => $other->id));
|
||||
return ($sub instanceof Subscription);
|
||||
try {
|
||||
$sub = self::getSubscription($subscriber, $other);
|
||||
} catch (NoResultException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static function getSubscription(Profile $subscriber, Profile $other)
|
||||
{
|
||||
// This is essentially a pkeyGet but we have an object to return in NoResultException
|
||||
$sub = new Subscription();
|
||||
$sub->subscriber = $subscriber->id;
|
||||
$sub->subscribed = $other->id;
|
||||
if (!$sub->find(true)) {
|
||||
throw new NoResultException($sub);
|
||||
}
|
||||
return $sub;
|
||||
}
|
||||
|
||||
function asActivity()
|
||||
|
@ -36,6 +36,9 @@ class Subscription_queue extends Managed_DataObject
|
||||
|
||||
public static function saveNew(Profile $subscriber, Profile $subscribed)
|
||||
{
|
||||
if (self::exists($subscriber, $subscribed)) {
|
||||
throw new AlreadyFulfilledException(_('This subscription request is already in progress.'));
|
||||
}
|
||||
$rq = new Subscription_queue();
|
||||
$rq->subscriber = $subscriber->id;
|
||||
$rq->subscribed = $subscribed->id;
|
||||
@ -51,6 +54,18 @@ class Subscription_queue extends Managed_DataObject
|
||||
return ($sub instanceof Subscription_queue);
|
||||
}
|
||||
|
||||
static function getSubQueue(Profile $subscriber, Profile $other)
|
||||
{
|
||||
// This is essentially a pkeyGet but we have an object to return in NoResultException
|
||||
$sub = new Subscription_queue();
|
||||
$sub->subscriber = $subscriber->id;
|
||||
$sub->subscribed = $other->id;
|
||||
if (!$sub->find(true)) {
|
||||
throw new NoResultException($sub);
|
||||
}
|
||||
return $sub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete a pending subscription, as we've got approval of some sort.
|
||||
*
|
||||
|
@ -10,13 +10,13 @@ class Token extends Managed_DataObject
|
||||
/* the code below is auto generated do not remove the above tag */
|
||||
|
||||
public $__table = 'token'; // table name
|
||||
public $consumer_key; // varchar(255) primary_key not_null
|
||||
public $consumer_key; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space
|
||||
public $tok; // char(32) primary_key not_null
|
||||
public $secret; // char(32) not_null
|
||||
public $type; // tinyint(1) not_null
|
||||
public $state; // tinyint(1)
|
||||
public $verifier; // varchar(255)
|
||||
public $verified_callback; // varchar(255)
|
||||
public $verifier; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $verified_callback; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime() not_null
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
@ -27,13 +27,13 @@ class Token extends Managed_DataObject
|
||||
return array(
|
||||
'description' => 'OAuth token record',
|
||||
'fields' => array(
|
||||
'consumer_key' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'unique identifier, root URL'),
|
||||
'consumer_key' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'unique identifier, root URL'),
|
||||
'tok' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'identifying value'),
|
||||
'secret' => array('type' => 'char', 'length' => 32, 'not null' => true, 'description' => 'secret value'),
|
||||
'type' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'request or access'),
|
||||
'state' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'for requests, 0 = initial, 1 = authorized, 2 = used'),
|
||||
'verifier' => array('type' => 'varchar', 'length' => 255, 'description' => 'verifier string for OAuth 1.0a'),
|
||||
'verified_callback' => array('type' => 'varchar', 'length' => 255, 'description' => 'verified callback URL for OAuth 1.0a'),
|
||||
'verifier' => array('type' => 'varchar', 'length' => 191, 'description' => 'verifier string for OAuth 1.0a'),
|
||||
'verified_callback' => array('type' => 'varchar', 'length' => 191, 'description' => 'verified callback URL for OAuth 1.0a'),
|
||||
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
|
@ -34,9 +34,9 @@ class User extends Managed_DataObject
|
||||
public $__table = 'user'; // table name
|
||||
public $id; // int(4) primary_key not_null
|
||||
public $nickname; // varchar(64) unique_key
|
||||
public $password; // varchar(255)
|
||||
public $email; // varchar(255) unique_key
|
||||
public $incomingemail; // varchar(255) unique_key
|
||||
public $password; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $email; // varchar(191) unique_key not 255 because utf8mb4 takes more space
|
||||
public $incomingemail; // varchar(191) unique_key not 255 because utf8mb4 takes more space
|
||||
public $emailnotifysub; // tinyint(1) default_1
|
||||
public $emailnotifyfav; // tinyint(1) default_1
|
||||
public $emailnotifynudge; // tinyint(1) default_1
|
||||
@ -50,8 +50,8 @@ class User extends Managed_DataObject
|
||||
public $carrier; // int(4)
|
||||
public $smsnotify; // tinyint(1)
|
||||
public $smsreplies; // tinyint(1)
|
||||
public $smsemail; // varchar(255)
|
||||
public $uri; // varchar(255) unique_key
|
||||
public $smsemail; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
|
||||
public $autosubscribe; // tinyint(1)
|
||||
public $subscribe_policy; // tinyint(1)
|
||||
public $urlshorteningservice; // varchar(50) default_ur1.ca
|
||||
@ -69,9 +69,9 @@ class User extends Managed_DataObject
|
||||
'fields' => array(
|
||||
'id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'),
|
||||
'nickname' => array('type' => 'varchar', 'length' => 64, 'description' => 'nickname or username, duped in profile'),
|
||||
'password' => array('type' => 'varchar', 'length' => 255, 'description' => 'salted password, can be null for OpenID users'),
|
||||
'email' => array('type' => 'varchar', 'length' => 255, 'description' => 'email address for password recovery etc.'),
|
||||
'incomingemail' => array('type' => 'varchar', 'length' => 255, 'description' => 'email address for post-by-email'),
|
||||
'password' => array('type' => 'varchar', 'length' => 191, 'description' => 'salted password, can be null for OpenID users'),
|
||||
'email' => array('type' => 'varchar', 'length' => 191, 'description' => 'email address for password recovery etc.'),
|
||||
'incomingemail' => array('type' => 'varchar', 'length' => 191, 'description' => 'email address for post-by-email'),
|
||||
'emailnotifysub' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of subscriptions'),
|
||||
'emailnotifyfav' => array('type' => 'int', 'size' => 'tiny', 'default' => null, 'description' => 'Notify by email of favorites'),
|
||||
'emailnotifynudge' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of nudges'),
|
||||
@ -85,8 +85,8 @@ class User extends Managed_DataObject
|
||||
'carrier' => array('type' => 'int', 'description' => 'foreign key to sms_carrier'),
|
||||
'smsnotify' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'whether to send notices to SMS'),
|
||||
'smsreplies' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'whether to send notices to SMS on replies'),
|
||||
'smsemail' => array('type' => 'varchar', 'length' => 255, 'description' => 'built from sms and carrier'),
|
||||
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universally unique identifier, usually a tag URI'),
|
||||
'smsemail' => array('type' => 'varchar', 'length' => 191, 'description' => 'built from sms and carrier'),
|
||||
'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'),
|
||||
'autosubscribe' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'automatically subscribe to users who subscribe to us'),
|
||||
'subscribe_policy' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => '0 = anybody can subscribe; 1 = require approval'),
|
||||
'urlshorteningservice' => array('type' => 'varchar', 'length' => 50, 'default' => 'internal', 'description' => 'service to use for auto-shortening URLs'),
|
||||
@ -191,7 +191,8 @@ class User extends Managed_DataObject
|
||||
* string 'password' (may be missing for eg OpenID registrations)
|
||||
* string 'code' invite code
|
||||
* ?string 'uri' permalink to notice; defaults to local notice URL
|
||||
* @return mixed User object or false on failure
|
||||
* @return User object
|
||||
* @throws Exception on failure
|
||||
*/
|
||||
static function register(array $fields) {
|
||||
|
||||
@ -205,12 +206,8 @@ class User extends Managed_DataObject
|
||||
$email = common_canonical_email($email);
|
||||
}
|
||||
|
||||
try {
|
||||
// Normalize _and_ check whether it is in use. Throw NicknameException on failure.
|
||||
$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);
|
||||
|
||||
@ -277,7 +274,9 @@ class User extends Managed_DataObject
|
||||
$id = $profile->insert();
|
||||
if ($id === false) {
|
||||
common_log_db_error($profile, 'INSERT', __FILE__);
|
||||
return false;
|
||||
$profile->query('ROLLBACK');
|
||||
// TRANS: Profile data could not be inserted for some reason.
|
||||
throw new ServerException(_m('Could not insert profile data for new user.'));
|
||||
}
|
||||
|
||||
$user->id = $id;
|
||||
@ -297,7 +296,8 @@ class User extends Managed_DataObject
|
||||
if ($result === false) {
|
||||
common_log_db_error($user, 'INSERT', __FILE__);
|
||||
$profile->query('ROLLBACK');
|
||||
return false;
|
||||
// TRANS: User data could not be inserted for some reason.
|
||||
throw new ServerException(_m('Could not insert user data for new user.'));
|
||||
}
|
||||
|
||||
// Everyone is subscribed to themself
|
||||
@ -312,7 +312,8 @@ class User extends Managed_DataObject
|
||||
if (!$result) {
|
||||
common_log_db_error($subscription, 'INSERT', __FILE__);
|
||||
$profile->query('ROLLBACK');
|
||||
return false;
|
||||
// TRANS: Subscription data could not be inserted for some reason.
|
||||
throw new ServerException(_m('Could not insert subscription data for new user.'));
|
||||
}
|
||||
|
||||
// Mark that this invite was converted
|
||||
@ -334,7 +335,8 @@ class User extends Managed_DataObject
|
||||
if (!$result) {
|
||||
common_log_db_error($confirm, 'INSERT', __FILE__);
|
||||
$profile->query('ROLLBACK');
|
||||
return false;
|
||||
// TRANS: Email confirmation data could not be inserted for some reason.
|
||||
throw new ServerException(_m('Could not insert email confirmation data for new user.'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -352,7 +354,7 @@ class User extends Managed_DataObject
|
||||
common_log(LOG_WARNING, sprintf("Default user %s does not exist.", $defnick),
|
||||
__FILE__);
|
||||
} else {
|
||||
Subscription::start($profile, $defuser->getProfile());
|
||||
Subscription::ensureStart($profile, $defuser->getProfile());
|
||||
}
|
||||
}
|
||||
|
||||
@ -385,6 +387,10 @@ class User extends Managed_DataObject
|
||||
Event::handle('EndUserRegister', array($profile));
|
||||
}
|
||||
|
||||
if (!$user instanceof User) {
|
||||
throw new ServerException('User could not be registered. Probably an event hook that failed.');
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
@ -687,11 +693,9 @@ class User extends Managed_DataObject
|
||||
return $stream->getNotices($offset, $limit, $since_id, $max_id);
|
||||
}
|
||||
|
||||
|
||||
function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null)
|
||||
public function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null)
|
||||
{
|
||||
// TRANS: Exception thrown when trying view "repeated to me".
|
||||
throw new Exception(_('Not implemented since inbox change.'));
|
||||
return $this->getProfile()->repeatedToMe($offset, $limit, $since_id, $max_id);
|
||||
}
|
||||
|
||||
public static function siteOwner()
|
||||
@ -994,6 +998,11 @@ class User extends Managed_DataObject
|
||||
return $act;
|
||||
}
|
||||
|
||||
public function isPrivateStream()
|
||||
{
|
||||
return $this->getProfile()->isPrivateStream();
|
||||
}
|
||||
|
||||
public function delPref($namespace, $topic)
|
||||
{
|
||||
return $this->getProfile()->delPref($namespace, $topic);
|
||||
|
@ -15,18 +15,18 @@ class User_group extends Managed_DataObject
|
||||
public $__table = 'user_group'; // table name
|
||||
public $id; // int(4) primary_key not_null
|
||||
public $nickname; // varchar(64)
|
||||
public $fullname; // varchar(255)
|
||||
public $homepage; // varchar(255)
|
||||
public $fullname; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $homepage; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $description; // text
|
||||
public $location; // varchar(255)
|
||||
public $original_logo; // varchar(255)
|
||||
public $homepage_logo; // varchar(255)
|
||||
public $stream_logo; // varchar(255)
|
||||
public $mini_logo; // varchar(255)
|
||||
public $location; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $original_logo; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $homepage_logo; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $stream_logo; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $mini_logo; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
|
||||
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
|
||||
public $uri; // varchar(255) unique_key
|
||||
public $mainpage; // varchar(255)
|
||||
public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
|
||||
public $mainpage; // varchar(191) not 255 because utf8mb4 takes more space
|
||||
public $join_policy; // tinyint
|
||||
public $force_scope; // tinyint
|
||||
|
||||
@ -41,21 +41,21 @@ class User_group extends Managed_DataObject
|
||||
'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'),
|
||||
|
||||
'nickname' => array('type' => 'varchar', 'length' => 64, 'description' => 'nickname for addressing'),
|
||||
'fullname' => array('type' => 'varchar', 'length' => 255, 'description' => 'display name'),
|
||||
'homepage' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL, cached so we dont regenerate'),
|
||||
'fullname' => array('type' => 'varchar', 'length' => 191, 'description' => 'display name'),
|
||||
'homepage' => array('type' => 'varchar', 'length' => 191, 'description' => 'URL, cached so we dont regenerate'),
|
||||
'description' => array('type' => 'text', 'description' => 'group description'),
|
||||
'location' => array('type' => 'varchar', 'length' => 255, 'description' => 'related physical location, if any'),
|
||||
'location' => array('type' => 'varchar', 'length' => 191, 'description' => 'related physical location, if any'),
|
||||
|
||||
'original_logo' => array('type' => 'varchar', 'length' => 255, 'description' => 'original size logo'),
|
||||
'homepage_logo' => array('type' => 'varchar', 'length' => 255, 'description' => 'homepage (profile) size logo'),
|
||||
'stream_logo' => array('type' => 'varchar', 'length' => 255, 'description' => 'stream-sized logo'),
|
||||
'mini_logo' => array('type' => 'varchar', 'length' => 255, 'description' => 'mini logo'),
|
||||
'original_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'original size logo'),
|
||||
'homepage_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'homepage (profile) size logo'),
|
||||
'stream_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'stream-sized logo'),
|
||||
'mini_logo' => array('type' => 'varchar', 'length' => 191, 'description' => 'mini logo'),
|
||||
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
|
||||
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universal identifier'),
|
||||
'mainpage' => array('type' => 'varchar', 'length' => 255, 'description' => 'page for group info to link to'),
|
||||
'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'),
|
||||
'mainpage' => array('type' => 'varchar', 'length' => 191, 'description' => 'page for group info to link to'),
|
||||
'join_policy' => array('type' => 'int', 'size' => 'tiny', 'description' => '0=open; 1=requires admin approval'),
|
||||
'force_scope' => array('type' => 'int', 'size' => 'tiny', 'description' => '0=never,1=sometimes,-1=always'),
|
||||
),
|
||||
@ -312,13 +312,21 @@ class User_group extends Managed_DataObject
|
||||
|
||||
function setOriginal($filename)
|
||||
{
|
||||
$imagefile = new ImageFile($this->id, Avatar::path($filename));
|
||||
// This should be handled by the Profile->setOriginal function so user and group avatars are handled the same
|
||||
$imagefile = new ImageFile(null, Avatar::path($filename));
|
||||
|
||||
$sizes = array('homepage_logo' => AVATAR_PROFILE_SIZE,
|
||||
'stream_logo' => AVATAR_STREAM_SIZE,
|
||||
'mini_logo' => AVATAR_MINI_SIZE);
|
||||
|
||||
$orig = clone($this);
|
||||
$this->original_logo = Avatar::url($filename);
|
||||
$this->homepage_logo = Avatar::url($imagefile->resize(AVATAR_PROFILE_SIZE));
|
||||
$this->stream_logo = Avatar::url($imagefile->resize(AVATAR_STREAM_SIZE));
|
||||
$this->mini_logo = Avatar::url($imagefile->resize(AVATAR_MINI_SIZE));
|
||||
foreach ($sizes as $name=>$size) {
|
||||
$filename = Avatar::filename($this->profile_id, image_type_to_extension($imagefile->preferredType()),
|
||||
$size, common_timestamp());
|
||||
$imagefile->resizeTo(Avatar::path($filename), array('width'=>$size, 'height'=>$size));
|
||||
$this->$name = Avatar::url($filename);
|
||||
}
|
||||
common_debug(common_log_objstring($this));
|
||||
return $this->update($orig);
|
||||
}
|
||||
|
@ -36,8 +36,8 @@ class User_im_prefs extends Managed_DataObject
|
||||
|
||||
public $__table = 'user_im_prefs'; // table name
|
||||
public $user_id; // int(4) primary_key not_null
|
||||
public $screenname; // varchar(255) not_null
|
||||
public $transport; // varchar(255) not_null
|
||||
public $screenname; // varchar(191) not_null not 255 because utf8mb4 takes more space
|
||||
public $transport; // varchar(191) not_null not 255 because utf8mb4 takes more space
|
||||
public $notify; // tinyint(1)
|
||||
public $replies; // tinyint(1)
|
||||
public $microid; // tinyint(1)
|
||||
@ -53,8 +53,8 @@ class User_im_prefs extends Managed_DataObject
|
||||
return array(
|
||||
'fields' => array(
|
||||
'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user'),
|
||||
'screenname' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'screenname on this service'),
|
||||
'transport' => array('type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'transport (ex xmpp, aim)'),
|
||||
'screenname' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'screenname on this service'),
|
||||
'transport' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'transport (ex xmpp, aim)'),
|
||||
'notify' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'Notify when a new notice is sent'),
|
||||
'replies' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 0, 'description' => 'Send replies from people not subscribed to'),
|
||||
'microid' => array('type' => 'int', 'size' => 'tiny', 'not null' => true, 'default' => 1, 'description' => 'Publish a MicroID'),
|
||||
|
@ -11,8 +11,8 @@ class User_username extends Managed_DataObject
|
||||
|
||||
public $__table = 'user_username'; // table name
|
||||
public $user_id; // int(4) not_null
|
||||
public $provider_name; // varchar(255) primary_key not_null
|
||||
public $username; // varchar(255) primary_key not_null
|
||||
public $provider_name; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space
|
||||
public $username; // varchar(191) primary_key not_null not 255 because utf8mb4 takes more space
|
||||
public $created; // datetime() not_null
|
||||
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
|
||||
|
||||
@ -23,8 +23,8 @@ class User_username extends Managed_DataObject
|
||||
{
|
||||
return array(
|
||||
'fields' => array(
|
||||
'provider_name' => array('type' => 'varchar', 'length' => 255, 'description' => 'provider name'),
|
||||
'username' => array('type' => 'varchar', 'length' => 255, 'description' => 'username'),
|
||||
'provider_name' => array('type' => 'varchar', 'length' => 191, 'description' => 'provider name'),
|
||||
'username' => array('type' => 'varchar', 'length' => 191, 'description' => 'username'),
|
||||
'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice id this title relates to'),
|
||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||
|
@ -21,7 +21,7 @@ create table status_network (
|
||||
created datetime not null comment 'date this record was created',
|
||||
modified timestamp comment 'date this record was modified'
|
||||
|
||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||||
|
||||
create table status_network_tag (
|
||||
site_id integer comment 'unique id',
|
||||
@ -30,5 +30,5 @@ create table status_network_tag (
|
||||
|
||||
constraint primary key (site_id, tag),
|
||||
index status_network_tag_tag_idx (tag)
|
||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||||
|
||||
|
@ -1,11 +1,31 @@
|
||||
### GNU social "fancy URL" setup
|
||||
#
|
||||
# Change the "RewriteBase" in the new .htaccess file to be the URL path
|
||||
# to your GNU Social installation on your server. Typically this will
|
||||
# be the path to your GNU Social directory relative to your Web root.
|
||||
# If you are installing it in the root directory, leave it as '/'.
|
||||
#
|
||||
# If it doesn't work, double-check that AllowOverride for the GNU Social
|
||||
# directory is 'All' in your Apache configuration file. This can be
|
||||
# * /etc/apache2/apache2.conf (generic)
|
||||
# * /etc/apache2/sites-available/default(on Debian and Ubuntu)
|
||||
# * ...many other variations depending on distribution...
|
||||
#
|
||||
# See the Apache documentation for .htaccess files for more details:
|
||||
# https://httpd.apache.org/docs/2.4/howto/htaccess.html
|
||||
#
|
||||
# Also, check that mod_rewrite is installed and enabled:
|
||||
# https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html
|
||||
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
|
||||
# NOTE: change this to your actual StatusNet base URL path,
|
||||
# NOTE: change this to your actual GNU social base URL path,
|
||||
# minus the domain part:
|
||||
#
|
||||
# http://example.com/ => /
|
||||
# http://example.com/mublog/ => /mublog/
|
||||
# https://social.example.com/ => /
|
||||
# https://example.com/social/ => /social/
|
||||
#
|
||||
RewriteBase /
|
||||
#RewriteBase /mublog/
|
||||
@ -26,7 +46,7 @@
|
||||
|
||||
<FilesMatch "\.(ini)">
|
||||
# For mod_access_compat in Apache <2.4
|
||||
Order allow,deny
|
||||
#Order allow,deny
|
||||
|
||||
# Use this instead for Apache >2.4 (mod_authz_host)
|
||||
# Require all denied
|
||||
|
@ -265,7 +265,7 @@ function main()
|
||||
$site_ssl = common_config('site', 'ssl');
|
||||
|
||||
// If the request is HTTP and it should be HTTPS...
|
||||
if ($site_ssl != 'never' && !StatusNet::isHTTPS() && common_is_sensitive($args['action'])) {
|
||||
if ($site_ssl != 'never' && !GNUsocial::isHTTPS() && common_is_sensitive($args['action'])) {
|
||||
common_redirect(common_local_url($args['action'], $args));
|
||||
}
|
||||
|
||||
|
@ -265,10 +265,10 @@ class WebInstaller extends Installer
|
||||
<li>
|
||||
<label for="site_profile">Type of site</label>
|
||||
<select id="site_profile" name="site_profile">
|
||||
<option value="private">Private</option>
|
||||
<option value="community">Community</option>
|
||||
<option value ="public">Public</option>
|
||||
<option value ="singleuser">Single User</option>
|
||||
<option value="public">Public (open registration)</option>
|
||||
<option value="singleuser">Single User</option>
|
||||
<option value="private">Private (no federation)</option>
|
||||
</select>
|
||||
<p class="form_guide">Initial access settings for your site</p>
|
||||
</li>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 212 B After Width: | Height: | Size: 212 B |
Before Width: | Height: | Size: 208 B After Width: | Height: | Size: 208 B |
Before Width: | Height: | Size: 335 B After Width: | Height: | Size: 335 B |
Before Width: | Height: | Size: 207 B After Width: | Height: | Size: 207 B |
Before Width: | Height: | Size: 262 B After Width: | Height: | Size: 262 B |
Before Width: | Height: | Size: 262 B After Width: | Height: | Size: 262 B |
Before Width: | Height: | Size: 332 B After Width: | Height: | Size: 332 B |
Before Width: | Height: | Size: 280 B After Width: | Height: | Size: 280 B |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
202
js/extlib/jquery-ui/css/smoothness/jquery-ui.css
vendored
@ -1,8 +1,8 @@
|
||||
/*! jQuery UI - v1.10.3 - 2013-09-12
|
||||
/*! jQuery UI - v1.11.3 - 2015-03-07
|
||||
* http://jqueryui.com
|
||||
* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css, jquery.ui.theme.css
|
||||
* Includes: core.css, draggable.css, resizable.css, selectable.css, sortable.css, accordion.css, autocomplete.css, button.css, datepicker.css, dialog.css, menu.css, progressbar.css, selectmenu.css, slider.css, spinner.css, tabs.css, tooltip.css, theme.css
|
||||
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
|
||||
* Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */
|
||||
* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */
|
||||
|
||||
/* Layout helpers
|
||||
----------------------------------*/
|
||||
@ -48,7 +48,7 @@
|
||||
left: 0;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
filter:Alpha(Opacity=0);
|
||||
filter:Alpha(Opacity=0); /* support: IE8 */
|
||||
}
|
||||
|
||||
.ui-front {
|
||||
@ -86,6 +86,10 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.ui-draggable-handle {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-resizable {
|
||||
position: relative;
|
||||
}
|
||||
@ -93,6 +97,8 @@
|
||||
position: absolute;
|
||||
font-size: 0.1px;
|
||||
display: block;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-resizable-disabled .ui-resizable-handle,
|
||||
.ui-resizable-autohide .ui-resizable-handle {
|
||||
@ -154,25 +160,31 @@
|
||||
right: -5px;
|
||||
top: -5px;
|
||||
}
|
||||
.ui-selectable {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-selectable-helper {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
border: 1px dotted black;
|
||||
}
|
||||
.ui-sortable-handle {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-accordion .ui-accordion-header {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
margin-top: 2px;
|
||||
margin: 2px 0 0 0;
|
||||
padding: .5em .5em .5em .7em;
|
||||
min-height: 0; /* support: IE7 */
|
||||
font-size: 100%;
|
||||
}
|
||||
.ui-accordion .ui-accordion-icons {
|
||||
padding-left: 2.2em;
|
||||
}
|
||||
.ui-accordion .ui-accordion-noicons {
|
||||
padding-left: .7em;
|
||||
}
|
||||
.ui-accordion .ui-accordion-icons .ui-accordion-icons {
|
||||
padding-left: 2.2em;
|
||||
}
|
||||
@ -347,12 +359,9 @@ button.ui-button::-moz-focus-inner {
|
||||
font-size: 1em;
|
||||
margin: 1px 0;
|
||||
}
|
||||
.ui-datepicker select.ui-datepicker-month-year {
|
||||
width: 100%;
|
||||
}
|
||||
.ui-datepicker select.ui-datepicker-month,
|
||||
.ui-datepicker select.ui-datepicker-year {
|
||||
width: 49%;
|
||||
width: 45%;
|
||||
}
|
||||
.ui-datepicker table {
|
||||
width: 100%;
|
||||
@ -466,6 +475,7 @@ button.ui-button::-moz-focus-inner {
|
||||
border-left-width: 1px;
|
||||
}
|
||||
.ui-dialog {
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -488,7 +498,7 @@ button.ui-button::-moz-focus-inner {
|
||||
position: absolute;
|
||||
right: .3em;
|
||||
top: 50%;
|
||||
width: 21px;
|
||||
width: 20px;
|
||||
margin: -10px 0 0 0;
|
||||
padding: 1px;
|
||||
height: 20px;
|
||||
@ -526,72 +536,56 @@ button.ui-button::-moz-focus-inner {
|
||||
}
|
||||
.ui-menu {
|
||||
list-style: none;
|
||||
padding: 2px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: block;
|
||||
outline: none;
|
||||
}
|
||||
.ui-menu .ui-menu {
|
||||
margin-top: -3px;
|
||||
position: absolute;
|
||||
}
|
||||
.ui-menu .ui-menu-item {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
padding: 3px 1em 3px .4em;
|
||||
cursor: pointer;
|
||||
min-height: 0; /* support: IE7 */
|
||||
/* support: IE10, see #8844 */
|
||||
list-style-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
|
||||
list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
|
||||
}
|
||||
.ui-menu .ui-menu-divider {
|
||||
margin: 5px -2px 5px -2px;
|
||||
margin: 5px 0;
|
||||
height: 0;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
border-width: 1px 0 0 0;
|
||||
}
|
||||
.ui-menu .ui-menu-item a {
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
padding: 2px .4em;
|
||||
line-height: 1.5;
|
||||
min-height: 0; /* support: IE7 */
|
||||
font-weight: normal;
|
||||
}
|
||||
.ui-menu .ui-menu-item a.ui-state-focus,
|
||||
.ui-menu .ui-menu-item a.ui-state-active {
|
||||
font-weight: normal;
|
||||
.ui-menu .ui-state-focus,
|
||||
.ui-menu .ui-state-active {
|
||||
margin: -1px;
|
||||
}
|
||||
|
||||
.ui-menu .ui-state-disabled {
|
||||
font-weight: normal;
|
||||
margin: .4em 0 .2em;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.ui-menu .ui-state-disabled a {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* icon support */
|
||||
.ui-menu-icons {
|
||||
position: relative;
|
||||
}
|
||||
.ui-menu-icons .ui-menu-item a {
|
||||
position: relative;
|
||||
.ui-menu-icons .ui-menu-item {
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
/* left-aligned */
|
||||
.ui-menu .ui-icon {
|
||||
position: absolute;
|
||||
top: .2em;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: .2em;
|
||||
margin: auto 0;
|
||||
}
|
||||
|
||||
/* right-aligned */
|
||||
.ui-menu .ui-menu-icon {
|
||||
position: static;
|
||||
float: right;
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
.ui-progressbar {
|
||||
height: 2em;
|
||||
@ -603,14 +597,63 @@ button.ui-button::-moz-focus-inner {
|
||||
height: 100%;
|
||||
}
|
||||
.ui-progressbar .ui-progressbar-overlay {
|
||||
background: url("images/animated-overlay.gif");
|
||||
background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");
|
||||
height: 100%;
|
||||
filter: alpha(opacity=25);
|
||||
filter: alpha(opacity=25); /* support: IE8 */
|
||||
opacity: 0.25;
|
||||
}
|
||||
.ui-progressbar-indeterminate .ui-progressbar-value {
|
||||
background-image: none;
|
||||
}
|
||||
.ui-selectmenu-menu {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: none;
|
||||
}
|
||||
.ui-selectmenu-menu .ui-menu {
|
||||
overflow: auto;
|
||||
/* Support: IE7 */
|
||||
overflow-x: hidden;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup {
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
line-height: 1.5;
|
||||
padding: 2px 0.4em;
|
||||
margin: 0.5em 0 0 0;
|
||||
height: auto;
|
||||
border: 0;
|
||||
}
|
||||
.ui-selectmenu-open {
|
||||
display: block;
|
||||
}
|
||||
.ui-selectmenu-button {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ui-selectmenu-button span.ui-icon {
|
||||
right: 0.5em;
|
||||
left: auto;
|
||||
margin-top: -8px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
}
|
||||
.ui-selectmenu-button span.ui-selectmenu-text {
|
||||
text-align: left;
|
||||
padding: 0.4em 2.1em 0.4em 1em;
|
||||
display: block;
|
||||
line-height: 1.4;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ui-slider {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
@ -621,6 +664,8 @@ button.ui-button::-moz-focus-inner {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
cursor: default;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-slider .ui-slider-range {
|
||||
position: absolute;
|
||||
@ -631,7 +676,7 @@ button.ui-button::-moz-focus-inner {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
/* For IE8 - See #6727 */
|
||||
/* support: IE8 - See #6727 */
|
||||
.ui-slider.ui-state-disabled .ui-slider-handle,
|
||||
.ui-slider.ui-state-disabled .ui-slider-range {
|
||||
filter: inherit;
|
||||
@ -704,13 +749,13 @@ button.ui-button::-moz-focus-inner {
|
||||
overflow: hidden;
|
||||
right: 0;
|
||||
}
|
||||
/* more specificity required here to overide default borders */
|
||||
/* more specificity required here to override default borders */
|
||||
.ui-spinner a.ui-spinner-button {
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
border-right: none;
|
||||
}
|
||||
/* vertical centre icon */
|
||||
/* vertically center icon */
|
||||
.ui-spinner .ui-icon {
|
||||
position: absolute;
|
||||
margin-top: -8px;
|
||||
@ -747,7 +792,7 @@ button.ui-button::-moz-focus-inner {
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ui-tabs .ui-tabs-nav li a {
|
||||
.ui-tabs .ui-tabs-nav .ui-tabs-anchor {
|
||||
float: left;
|
||||
padding: .5em 1em;
|
||||
text-decoration: none;
|
||||
@ -756,13 +801,12 @@ button.ui-button::-moz-focus-inner {
|
||||
margin-bottom: -1px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
.ui-tabs .ui-tabs-nav li.ui-tabs-active a,
|
||||
.ui-tabs .ui-tabs-nav li.ui-state-disabled a,
|
||||
.ui-tabs .ui-tabs-nav li.ui-tabs-loading a {
|
||||
.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,
|
||||
.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,
|
||||
.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor {
|
||||
cursor: text;
|
||||
}
|
||||
.ui-tabs .ui-tabs-nav li a, /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
|
||||
.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a {
|
||||
.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor {
|
||||
cursor: pointer;
|
||||
}
|
||||
.ui-tabs .ui-tabs-panel {
|
||||
@ -801,7 +845,7 @@ body .ui-tooltip {
|
||||
}
|
||||
.ui-widget-content {
|
||||
border: 1px solid #aaaaaa;
|
||||
background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x;
|
||||
background: #ffffff url("images/ui-bg_flat_75_ffffff_40x100.png") 50% 50% repeat-x;
|
||||
color: #222222;
|
||||
}
|
||||
.ui-widget-content a {
|
||||
@ -809,7 +853,7 @@ body .ui-tooltip {
|
||||
}
|
||||
.ui-widget-header {
|
||||
border: 1px solid #aaaaaa;
|
||||
background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;
|
||||
background: #cccccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x;
|
||||
color: #222222;
|
||||
font-weight: bold;
|
||||
}
|
||||
@ -823,7 +867,7 @@ body .ui-tooltip {
|
||||
.ui-widget-content .ui-state-default,
|
||||
.ui-widget-header .ui-state-default {
|
||||
border: 1px solid #d3d3d3;
|
||||
background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x;
|
||||
background: #e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x;
|
||||
font-weight: normal;
|
||||
color: #555555;
|
||||
}
|
||||
@ -840,14 +884,18 @@ body .ui-tooltip {
|
||||
.ui-widget-content .ui-state-focus,
|
||||
.ui-widget-header .ui-state-focus {
|
||||
border: 1px solid #999999;
|
||||
background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x;
|
||||
background: #dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x;
|
||||
font-weight: normal;
|
||||
color: #212121;
|
||||
}
|
||||
.ui-state-hover a,
|
||||
.ui-state-hover a:hover,
|
||||
.ui-state-hover a:link,
|
||||
.ui-state-hover a:visited {
|
||||
.ui-state-hover a:visited,
|
||||
.ui-state-focus a,
|
||||
.ui-state-focus a:hover,
|
||||
.ui-state-focus a:link,
|
||||
.ui-state-focus a:visited {
|
||||
color: #212121;
|
||||
text-decoration: none;
|
||||
}
|
||||
@ -855,7 +903,7 @@ body .ui-tooltip {
|
||||
.ui-widget-content .ui-state-active,
|
||||
.ui-widget-header .ui-state-active {
|
||||
border: 1px solid #aaaaaa;
|
||||
background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;
|
||||
background: #ffffff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;
|
||||
font-weight: normal;
|
||||
color: #212121;
|
||||
}
|
||||
@ -872,7 +920,7 @@ body .ui-tooltip {
|
||||
.ui-widget-content .ui-state-highlight,
|
||||
.ui-widget-header .ui-state-highlight {
|
||||
border: 1px solid #fcefa1;
|
||||
background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x;
|
||||
background: #fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;
|
||||
color: #363636;
|
||||
}
|
||||
.ui-state-highlight a,
|
||||
@ -884,7 +932,7 @@ body .ui-tooltip {
|
||||
.ui-widget-content .ui-state-error,
|
||||
.ui-widget-header .ui-state-error {
|
||||
border: 1px solid #cd0a0a;
|
||||
background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x;
|
||||
background: #fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;
|
||||
color: #cd0a0a;
|
||||
}
|
||||
.ui-state-error a,
|
||||
@ -906,18 +954,18 @@ body .ui-tooltip {
|
||||
.ui-widget-content .ui-priority-secondary,
|
||||
.ui-widget-header .ui-priority-secondary {
|
||||
opacity: .7;
|
||||
filter:Alpha(Opacity=70);
|
||||
filter:Alpha(Opacity=70); /* support: IE8 */
|
||||
font-weight: normal;
|
||||
}
|
||||
.ui-state-disabled,
|
||||
.ui-widget-content .ui-state-disabled,
|
||||
.ui-widget-header .ui-state-disabled {
|
||||
opacity: .35;
|
||||
filter:Alpha(Opacity=35);
|
||||
filter:Alpha(Opacity=35); /* support: IE8 */
|
||||
background-image: none;
|
||||
}
|
||||
.ui-state-disabled .ui-icon {
|
||||
filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
|
||||
filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */
|
||||
}
|
||||
|
||||
/* Icons
|
||||
@ -930,27 +978,27 @@ body .ui-tooltip {
|
||||
}
|
||||
.ui-icon,
|
||||
.ui-widget-content .ui-icon {
|
||||
background-image: url(images/ui-icons_222222_256x240.png);
|
||||
background-image: url("images/ui-icons_222222_256x240.png");
|
||||
}
|
||||
.ui-widget-header .ui-icon {
|
||||
background-image: url(images/ui-icons_222222_256x240.png);
|
||||
background-image: url("images/ui-icons_222222_256x240.png");
|
||||
}
|
||||
.ui-state-default .ui-icon {
|
||||
background-image: url(images/ui-icons_888888_256x240.png);
|
||||
background-image: url("images/ui-icons_888888_256x240.png");
|
||||
}
|
||||
.ui-state-hover .ui-icon,
|
||||
.ui-state-focus .ui-icon {
|
||||
background-image: url(images/ui-icons_454545_256x240.png);
|
||||
background-image: url("images/ui-icons_454545_256x240.png");
|
||||
}
|
||||
.ui-state-active .ui-icon {
|
||||
background-image: url(images/ui-icons_454545_256x240.png);
|
||||
background-image: url("images/ui-icons_454545_256x240.png");
|
||||
}
|
||||
.ui-state-highlight .ui-icon {
|
||||
background-image: url(images/ui-icons_2e83ff_256x240.png);
|
||||
background-image: url("images/ui-icons_2e83ff_256x240.png");
|
||||
}
|
||||
.ui-state-error .ui-icon,
|
||||
.ui-state-error-text .ui-icon {
|
||||
background-image: url(images/ui-icons_cd0a0a_256x240.png);
|
||||
background-image: url("images/ui-icons_cd0a0a_256x240.png");
|
||||
}
|
||||
|
||||
/* positioning */
|
||||
@ -1163,15 +1211,15 @@ body .ui-tooltip {
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay {
|
||||
background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;
|
||||
background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;
|
||||
opacity: .3;
|
||||
filter: Alpha(Opacity=30);
|
||||
filter: Alpha(Opacity=30); /* support: IE8 */
|
||||
}
|
||||
.ui-widget-shadow {
|
||||
margin: -8px 0 0 -8px;
|
||||
padding: 8px;
|
||||
background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;
|
||||
background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;
|
||||
opacity: .3;
|
||||
filter: Alpha(Opacity=30);
|
||||
filter: Alpha(Opacity=30); /* support: IE8 */
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
6321
js/extlib/jquery-ui/jquery-ui.js
vendored
@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* jQuery Cookie Plugin v1.3.1
|
||||
* jQuery Cookie Plugin v1.4.1
|
||||
* https://github.com/carhartl/jquery-cookie
|
||||
*
|
||||
* Copyright 2013 Klaus Hartl
|
||||
@ -7,57 +7,65 @@
|
||||
*/
|
||||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
// AMD
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
// Browser globals
|
||||
factory(jQuery);
|
||||
}
|
||||
}(function ($) {
|
||||
|
||||
var pluses = /\+/g;
|
||||
|
||||
function decode(s) {
|
||||
if (config.raw) {
|
||||
return s;
|
||||
}
|
||||
try {
|
||||
// If we can't decode the cookie, ignore it, it's unusable.
|
||||
return decodeURIComponent(s.replace(pluses, ' '));
|
||||
} catch(e) {}
|
||||
function encode(s) {
|
||||
return config.raw ? s : encodeURIComponent(s);
|
||||
}
|
||||
|
||||
function decodeAndParse(s) {
|
||||
function decode(s) {
|
||||
return config.raw ? s : decodeURIComponent(s);
|
||||
}
|
||||
|
||||
function stringifyCookieValue(value) {
|
||||
return encode(config.json ? JSON.stringify(value) : String(value));
|
||||
}
|
||||
|
||||
function parseCookieValue(s) {
|
||||
if (s.indexOf('"') === 0) {
|
||||
// This is a quoted cookie as according to RFC2068, unescape...
|
||||
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
||||
}
|
||||
|
||||
s = decode(s);
|
||||
|
||||
try {
|
||||
// Replace server-side written pluses with spaces.
|
||||
// If we can't decode the cookie, ignore it, it's unusable.
|
||||
// If we can't parse the cookie, ignore it, it's unusable.
|
||||
s = decodeURIComponent(s.replace(pluses, ' '));
|
||||
return config.json ? JSON.parse(s) : s;
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
function read(s, converter) {
|
||||
var value = config.raw ? s : parseCookieValue(s);
|
||||
return $.isFunction(converter) ? converter(value) : value;
|
||||
}
|
||||
|
||||
var config = $.cookie = function (key, value, options) {
|
||||
|
||||
// Write
|
||||
if (value !== undefined) {
|
||||
|
||||
if (value !== undefined && !$.isFunction(value)) {
|
||||
options = $.extend({}, config.defaults, options);
|
||||
|
||||
if (typeof options.expires === 'number') {
|
||||
var days = options.expires, t = options.expires = new Date();
|
||||
t.setDate(t.getDate() + days);
|
||||
t.setTime(+t + days * 864e+5);
|
||||
}
|
||||
|
||||
value = config.json ? JSON.stringify(value) : String(value);
|
||||
|
||||
return (document.cookie = [
|
||||
config.raw ? key : encodeURIComponent(key),
|
||||
'=',
|
||||
config.raw ? value : encodeURIComponent(value),
|
||||
encode(key), '=', stringifyCookieValue(value),
|
||||
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
|
||||
options.path ? '; path=' + options.path : '',
|
||||
options.domain ? '; domain=' + options.domain : '',
|
||||
@ -80,12 +88,13 @@
|
||||
var cookie = parts.join('=');
|
||||
|
||||
if (key && key === name) {
|
||||
result = decodeAndParse(cookie);
|
||||
// If second argument (value) is a function it's a converter...
|
||||
result = read(cookie, value);
|
||||
break;
|
||||
}
|
||||
|
||||
// Prevent storing a cookie that we couldn't decode.
|
||||
if (!key && (cookie = decodeAndParse(cookie)) !== undefined) {
|
||||
if (!key && (cookie = read(cookie)) !== undefined) {
|
||||
result[name] = cookie;
|
||||
}
|
||||
}
|
||||
@ -96,12 +105,13 @@
|
||||
config.defaults = {};
|
||||
|
||||
$.removeCookie = function (key, options) {
|
||||
if ($.cookie(key) !== undefined) {
|
||||
if ($.cookie(key) === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must not alter options, thus extending a fresh object...
|
||||
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return !$.cookie(key);
|
||||
};
|
||||
|
||||
}));
|
||||
|
@ -1,15 +1,28 @@
|
||||
/*!
|
||||
* jQuery Form Plugin
|
||||
* version: 3.43.0-2013.09.03
|
||||
* version: 3.51.0-2014.06.20
|
||||
* Requires jQuery v1.5 or later
|
||||
* Copyright (c) 2013 M. Alsup
|
||||
* Copyright (c) 2014 M. Alsup
|
||||
* Examples and documentation at: http://malsup.com/jquery/form/
|
||||
* Project repository: https://github.com/malsup/form
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
* https://github.com/malsup/form#copyright-and-license
|
||||
*/
|
||||
/*global ActiveXObject */
|
||||
;(function($) {
|
||||
|
||||
// AMD support
|
||||
(function (factory) {
|
||||
"use strict";
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// using AMD; register as anon module
|
||||
define(['jquery'], factory);
|
||||
} else {
|
||||
// no AMD; invoke directly
|
||||
factory( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto );
|
||||
}
|
||||
}
|
||||
|
||||
(function($) {
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
@ -63,11 +76,13 @@ var hasProp = !!$.fn.prop;
|
||||
// contains inputs with names like "action" or "method"; in those
|
||||
// cases "prop" returns the element
|
||||
$.fn.attr2 = function() {
|
||||
if ( ! hasProp )
|
||||
if ( ! hasProp ) {
|
||||
return this.attr.apply(this, arguments);
|
||||
}
|
||||
var val = this.prop.apply(this, arguments);
|
||||
if ( ( val && val.jquery ) || typeof val === 'string' )
|
||||
if ( ( val && val.jquery ) || typeof val === 'string' ) {
|
||||
return val;
|
||||
}
|
||||
return this.attr.apply(this, arguments);
|
||||
};
|
||||
|
||||
@ -209,7 +224,7 @@ $.fn.ajaxSubmit = function(options) {
|
||||
|
||||
// [value] (issue #113), also see comment:
|
||||
// https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
|
||||
var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() != ''; });
|
||||
var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() !== ''; });
|
||||
|
||||
var hasFileInputs = fileInputs.length > 0;
|
||||
var mp = 'multipart/form-data';
|
||||
@ -245,8 +260,9 @@ $.fn.ajaxSubmit = function(options) {
|
||||
$form.removeData('jqxhr').data('jqxhr', jqxhr);
|
||||
|
||||
// clear element array
|
||||
for (var k=0; k < elements.length; k++)
|
||||
for (var k=0; k < elements.length; k++) {
|
||||
elements[k] = null;
|
||||
}
|
||||
|
||||
// fire 'notify' event
|
||||
this.trigger('form-submit-notify', [this, options]);
|
||||
@ -278,10 +294,12 @@ $.fn.ajaxSubmit = function(options) {
|
||||
|
||||
if (options.extraData) {
|
||||
var serializedData = deepSerialize(options.extraData);
|
||||
for (i=0; i < serializedData.length; i++)
|
||||
if (serializedData[i])
|
||||
for (i=0; i < serializedData.length; i++) {
|
||||
if (serializedData[i]) {
|
||||
formdata.append(serializedData[i][0], serializedData[i][1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
options.data = null;
|
||||
|
||||
@ -314,9 +332,16 @@ $.fn.ajaxSubmit = function(options) {
|
||||
s.data = null;
|
||||
var beforeSend = s.beforeSend;
|
||||
s.beforeSend = function(xhr, o) {
|
||||
//Send FormData() provided by user
|
||||
if (options.formData) {
|
||||
o.data = options.formData;
|
||||
}
|
||||
else {
|
||||
o.data = formdata;
|
||||
if(beforeSend)
|
||||
}
|
||||
if(beforeSend) {
|
||||
beforeSend.call(this, xhr, o);
|
||||
}
|
||||
};
|
||||
return $.ajax(s);
|
||||
}
|
||||
@ -335,12 +360,14 @@ $.fn.ajaxSubmit = function(options) {
|
||||
// ensure that every serialized input is still enabled
|
||||
for (i=0; i < elements.length; i++) {
|
||||
el = $(elements[i]);
|
||||
if ( hasProp )
|
||||
if ( hasProp ) {
|
||||
el.prop('disabled', false);
|
||||
else
|
||||
}
|
||||
else {
|
||||
el.removeAttr('disabled');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s = $.extend(true, {}, $.ajaxSettings, options);
|
||||
s.context = s.context || s;
|
||||
@ -348,11 +375,13 @@ $.fn.ajaxSubmit = function(options) {
|
||||
if (s.iframeTarget) {
|
||||
$io = $(s.iframeTarget);
|
||||
n = $io.attr2('name');
|
||||
if (!n)
|
||||
if (!n) {
|
||||
$io.attr2('name', id);
|
||||
else
|
||||
}
|
||||
else {
|
||||
id = n;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
|
||||
$io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
|
||||
@ -383,13 +412,16 @@ $.fn.ajaxSubmit = function(options) {
|
||||
|
||||
$io.attr('src', s.iframeSrc); // abort op in progress
|
||||
xhr.error = e;
|
||||
if (s.error)
|
||||
if (s.error) {
|
||||
s.error.call(s.context, xhr, e, status);
|
||||
if (g)
|
||||
}
|
||||
if (g) {
|
||||
$.event.trigger("ajaxError", [xhr, s, e]);
|
||||
if (s.complete)
|
||||
}
|
||||
if (s.complete) {
|
||||
s.complete.call(s.context, xhr, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
g = s.global;
|
||||
@ -475,7 +507,10 @@ $.fn.ajaxSubmit = function(options) {
|
||||
// take a breath so that pending repaints get some cpu time before the upload starts
|
||||
function doSubmit() {
|
||||
// make sure form attrs are set
|
||||
var t = $form.attr2('target'), a = $form.attr2('action');
|
||||
var t = $form.attr2('target'),
|
||||
a = $form.attr2('action'),
|
||||
mp = 'multipart/form-data',
|
||||
et = $form.attr('enctype') || $form.attr('encoding') || mp;
|
||||
|
||||
// update form attrs in IE friendly way
|
||||
form.setAttribute('target',id);
|
||||
@ -504,14 +539,16 @@ $.fn.ajaxSubmit = function(options) {
|
||||
try {
|
||||
var state = getDoc(io).readyState;
|
||||
log('state = ' + state);
|
||||
if (state && state.toLowerCase() == 'uninitialized')
|
||||
if (state && state.toLowerCase() == 'uninitialized') {
|
||||
setTimeout(checkState,50);
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
log('Server abort: ' , e, ' (', e.name, ')');
|
||||
cb(SERVER_ABORT);
|
||||
if (timeoutHandle)
|
||||
if (timeoutHandle) {
|
||||
clearTimeout(timeoutHandle);
|
||||
}
|
||||
timeoutHandle = undefined;
|
||||
}
|
||||
}
|
||||
@ -540,10 +577,12 @@ $.fn.ajaxSubmit = function(options) {
|
||||
// add iframe to doc and submit the form
|
||||
$io.appendTo('body');
|
||||
}
|
||||
if (io.attachEvent)
|
||||
if (io.attachEvent) {
|
||||
io.attachEvent('onload', cb);
|
||||
else
|
||||
}
|
||||
else {
|
||||
io.addEventListener('load', cb, false);
|
||||
}
|
||||
setTimeout(checkState,15);
|
||||
|
||||
try {
|
||||
@ -557,6 +596,7 @@ $.fn.ajaxSubmit = function(options) {
|
||||
finally {
|
||||
// reset attrs and remove "extra" input elements
|
||||
form.setAttribute('action',a);
|
||||
form.setAttribute('enctype', et); // #380
|
||||
if(t) {
|
||||
form.setAttribute('target', t);
|
||||
} else {
|
||||
@ -598,13 +638,16 @@ $.fn.ajaxSubmit = function(options) {
|
||||
|
||||
if (!doc || doc.location.href == s.iframeSrc) {
|
||||
// response not received yet
|
||||
if (!timedOut)
|
||||
if (!timedOut) {
|
||||
return;
|
||||
}
|
||||
if (io.detachEvent)
|
||||
}
|
||||
if (io.detachEvent) {
|
||||
io.detachEvent('onload', cb);
|
||||
else
|
||||
}
|
||||
else {
|
||||
io.removeEventListener('load', cb, false);
|
||||
}
|
||||
|
||||
var status = 'success', errMsg;
|
||||
try {
|
||||
@ -631,8 +674,9 @@ $.fn.ajaxSubmit = function(options) {
|
||||
var docRoot = doc.body ? doc.body : doc.documentElement;
|
||||
xhr.responseText = docRoot ? docRoot.innerHTML : null;
|
||||
xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
|
||||
if (isXml)
|
||||
if (isXml) {
|
||||
s.dataType = 'xml';
|
||||
}
|
||||
xhr.getResponseHeader = function(header){
|
||||
var headers = {'content-type': s.dataType};
|
||||
return headers[header.toLowerCase()];
|
||||
@ -695,42 +739,52 @@ $.fn.ajaxSubmit = function(options) {
|
||||
|
||||
// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
|
||||
if (status === 'success') {
|
||||
if (s.success)
|
||||
if (s.success) {
|
||||
s.success.call(s.context, data, 'success', xhr);
|
||||
}
|
||||
deferred.resolve(xhr.responseText, 'success', xhr);
|
||||
if (g)
|
||||
if (g) {
|
||||
$.event.trigger("ajaxSuccess", [xhr, s]);
|
||||
}
|
||||
}
|
||||
else if (status) {
|
||||
if (errMsg === undefined)
|
||||
if (errMsg === undefined) {
|
||||
errMsg = xhr.statusText;
|
||||
if (s.error)
|
||||
}
|
||||
if (s.error) {
|
||||
s.error.call(s.context, xhr, status, errMsg);
|
||||
}
|
||||
deferred.reject(xhr, 'error', errMsg);
|
||||
if (g)
|
||||
if (g) {
|
||||
$.event.trigger("ajaxError", [xhr, s, errMsg]);
|
||||
}
|
||||
}
|
||||
|
||||
if (g)
|
||||
if (g) {
|
||||
$.event.trigger("ajaxComplete", [xhr, s]);
|
||||
}
|
||||
|
||||
if (g && ! --$.active) {
|
||||
$.event.trigger("ajaxStop");
|
||||
}
|
||||
|
||||
if (s.complete)
|
||||
if (s.complete) {
|
||||
s.complete.call(s.context, xhr, status);
|
||||
}
|
||||
|
||||
callbackProcessed = true;
|
||||
if (s.timeout)
|
||||
if (s.timeout) {
|
||||
clearTimeout(timeoutHandle);
|
||||
}
|
||||
|
||||
// clean up
|
||||
setTimeout(function() {
|
||||
if (!s.iframeTarget)
|
||||
if (!s.iframeTarget) {
|
||||
$io.remove();
|
||||
else //adding else to clean up existing iframe response.
|
||||
}
|
||||
else { //adding else to clean up existing iframe response.
|
||||
$io.attr('src', s.iframeSrc);
|
||||
}
|
||||
xhr.responseXML = null;
|
||||
}, 100);
|
||||
}
|
||||
@ -758,9 +812,10 @@ $.fn.ajaxSubmit = function(options) {
|
||||
data = xml ? xhr.responseXML : xhr.responseText;
|
||||
|
||||
if (xml && data.documentElement.nodeName === 'parsererror') {
|
||||
if ($.error)
|
||||
if ($.error) {
|
||||
$.error('parsererror');
|
||||
}
|
||||
}
|
||||
if (s && s.dataFilter) {
|
||||
data = s.dataFilter(data, type);
|
||||
}
|
||||
@ -832,7 +887,7 @@ function doAjaxSubmit(e) {
|
||||
var options = e.data;
|
||||
if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
|
||||
e.preventDefault();
|
||||
$(this).ajaxSubmit(options);
|
||||
$(e.target).ajaxSubmit(options); // #365
|
||||
}
|
||||
}
|
||||
|
||||
@ -891,8 +946,23 @@ $.fn.formToArray = function(semantic, elements) {
|
||||
}
|
||||
|
||||
var form = this[0];
|
||||
var formId = this.attr('id');
|
||||
var els = semantic ? form.getElementsByTagName('*') : form.elements;
|
||||
if (!els) {
|
||||
var els2;
|
||||
|
||||
if (els && !/MSIE [678]/.test(navigator.userAgent)) { // #390
|
||||
els = $(els).get(); // convert to standard array
|
||||
}
|
||||
|
||||
// #386; account for inputs outside the form which use the 'form' attribute
|
||||
if ( formId ) {
|
||||
els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet
|
||||
if ( els2.length ) {
|
||||
els = (els || []).concat(els2);
|
||||
}
|
||||
}
|
||||
|
||||
if (!els || !els.length) {
|
||||
return a;
|
||||
}
|
||||
|
||||
@ -915,15 +985,17 @@ $.fn.formToArray = function(semantic, elements) {
|
||||
|
||||
v = $.fieldValue(el, true);
|
||||
if (v && v.constructor == Array) {
|
||||
if (elements)
|
||||
if (elements) {
|
||||
elements.push(el);
|
||||
}
|
||||
for(j=0, jmax=v.length; j < jmax; j++) {
|
||||
a.push({name: n, value: v[j]});
|
||||
}
|
||||
}
|
||||
else if (feature.fileapi && el.type == 'file') {
|
||||
if (elements)
|
||||
if (elements) {
|
||||
elements.push(el);
|
||||
}
|
||||
var files = el.files;
|
||||
if (files.length) {
|
||||
for (j=0; j < files.length; j++) {
|
||||
@ -936,8 +1008,9 @@ $.fn.formToArray = function(semantic, elements) {
|
||||
}
|
||||
}
|
||||
else if (v !== null && typeof v != 'undefined') {
|
||||
if (elements)
|
||||
if (elements) {
|
||||
elements.push(el);
|
||||
}
|
||||
a.push({name: n, value: v, type: el.type, required: el.required});
|
||||
}
|
||||
}
|
||||
@ -1033,11 +1106,13 @@ $.fn.fieldValue = function(successful) {
|
||||
if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
|
||||
continue;
|
||||
}
|
||||
if (v.constructor == Array)
|
||||
if (v.constructor == Array) {
|
||||
$.merge(val, v);
|
||||
else
|
||||
}
|
||||
else {
|
||||
val.push(v);
|
||||
}
|
||||
}
|
||||
return val;
|
||||
};
|
||||
|
||||
@ -1070,7 +1145,7 @@ $.fieldValue = function(el, successful) {
|
||||
if (op.selected) {
|
||||
var v = op.value;
|
||||
if (!v) { // extra pain for IE...
|
||||
v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
|
||||
v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value;
|
||||
}
|
||||
if (one) {
|
||||
return v;
|
||||
@ -1126,9 +1201,10 @@ $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
|
||||
// $('#myForm').clearForm('.special:hidden')
|
||||
// the above would clean hidden inputs that have the class of 'special'
|
||||
if ( (includeHidden === true && /hidden/.test(t)) ||
|
||||
(typeof includeHidden == 'string' && $(this).is(includeHidden)) )
|
||||
(typeof includeHidden == 'string' && $(this).is(includeHidden)) ) {
|
||||
this.value = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@ -1186,8 +1262,9 @@ $.fn.ajaxSubmit.debug = false;
|
||||
|
||||
// helper fn for console logging
|
||||
function log() {
|
||||
if (!$.fn.ajaxSubmit.debug)
|
||||
if (!$.fn.ajaxSubmit.debug) {
|
||||
return;
|
||||
}
|
||||
var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
|
||||
if (window.console && window.console.log) {
|
||||
window.console.log(msg);
|
||||
@ -1197,4 +1274,4 @@ function log() {
|
||||
}
|
||||
}
|
||||
|
||||
})( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto );
|
||||
}));
|
||||
|
@ -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));
|
7828
js/extlib/jquery.js
vendored
@ -1,6 +1,6 @@
|
||||
/*
|
||||
json2.js
|
||||
2013-05-26
|
||||
2015-02-25
|
||||
|
||||
Public Domain.
|
||||
|
||||
@ -48,7 +48,9 @@
|
||||
Date.prototype.toJSON = function (key) {
|
||||
function f(n) {
|
||||
// Format integers to have at least two digits.
|
||||
return n < 10 ? '0' + n : n;
|
||||
return n < 10
|
||||
? '0' + n
|
||||
: n;
|
||||
}
|
||||
|
||||
return this.getUTCFullYear() + '-' +
|
||||
@ -146,10 +148,12 @@
|
||||
redistribute.
|
||||
*/
|
||||
|
||||
/*jslint evil: true, regexp: true */
|
||||
/*jslint
|
||||
eval, for, this
|
||||
*/
|
||||
|
||||
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
|
||||
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
|
||||
/*property
|
||||
JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
|
||||
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
|
||||
lastIndex, length, parse, prototype, push, replace, slice, stringify,
|
||||
test, toJSON, toString, valueOf
|
||||
@ -168,7 +172,13 @@ if (typeof JSON !== 'object') {
|
||||
|
||||
function f(n) {
|
||||
// Format integers to have at least two digits.
|
||||
return n < 10 ? '0' + n : n;
|
||||
return n < 10
|
||||
? '0' + n
|
||||
: n;
|
||||
}
|
||||
|
||||
function this_value() {
|
||||
return this.valueOf();
|
||||
}
|
||||
|
||||
if (typeof Date.prototype.toJSON !== 'function') {
|
||||
@ -185,26 +195,16 @@ if (typeof JSON !== 'object') {
|
||||
: null;
|
||||
};
|
||||
|
||||
String.prototype.toJSON =
|
||||
Number.prototype.toJSON =
|
||||
Boolean.prototype.toJSON = function () {
|
||||
return this.valueOf();
|
||||
};
|
||||
Boolean.prototype.toJSON = this_value;
|
||||
Number.prototype.toJSON = this_value;
|
||||
String.prototype.toJSON = this_value;
|
||||
}
|
||||
|
||||
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
var cx,
|
||||
escapable,
|
||||
gap,
|
||||
indent,
|
||||
meta = { // table of character substitutions
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
},
|
||||
meta,
|
||||
rep;
|
||||
|
||||
|
||||
@ -216,12 +216,14 @@ if (typeof JSON !== 'object') {
|
||||
// sequences.
|
||||
|
||||
escapable.lastIndex = 0;
|
||||
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
|
||||
return escapable.test(string)
|
||||
? '"' + string.replace(escapable, function (a) {
|
||||
var c = meta[a];
|
||||
return typeof c === 'string'
|
||||
? c
|
||||
: '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
}) + '"' : '"' + string + '"';
|
||||
}) + '"'
|
||||
: '"' + string + '"';
|
||||
}
|
||||
|
||||
|
||||
@ -261,7 +263,9 @@ if (typeof JSON !== 'object') {
|
||||
|
||||
// JSON numbers must be finite. Encode non-finite numbers as null.
|
||||
|
||||
return isFinite(value) ? String(value) : 'null';
|
||||
return isFinite(value)
|
||||
? String(value)
|
||||
: 'null';
|
||||
|
||||
case 'boolean':
|
||||
case 'null':
|
||||
@ -322,7 +326,11 @@ if (typeof JSON !== 'object') {
|
||||
k = rep[i];
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
partial.push(quote(k) + (
|
||||
gap
|
||||
? ': '
|
||||
: ':'
|
||||
) + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -334,7 +342,11 @@ if (typeof JSON !== 'object') {
|
||||
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
partial.push(quote(k) + (
|
||||
gap
|
||||
? ': '
|
||||
: ':'
|
||||
) + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -356,6 +368,16 @@ if (typeof JSON !== 'object') {
|
||||
// If the JSON object does not yet have a stringify method, give it one.
|
||||
|
||||
if (typeof JSON.stringify !== 'function') {
|
||||
escapable = /[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
|
||||
meta = { // table of character substitutions
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"': '\\"',
|
||||
'\\': '\\\\'
|
||||
};
|
||||
JSON.stringify = function (value, replacer, space) {
|
||||
|
||||
// The stringify method takes a value and an optional replacer, and an optional
|
||||
@ -403,6 +425,7 @@ if (typeof JSON !== 'object') {
|
||||
// If the JSON object does not yet have a parse method, give it one.
|
||||
|
||||
if (typeof JSON.parse !== 'function') {
|
||||
cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
|
||||
JSON.parse = function (text, reviver) {
|
||||
|
||||
// The parse method takes a text and an optional reviver function, and returns
|
||||
@ -458,10 +481,13 @@ if (typeof JSON !== 'object') {
|
||||
// we look to see that the remaining characters are only whitespace or ']' or
|
||||
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
|
||||
|
||||
if (/^[\],:{}\s]*$/
|
||||
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
|
||||
if (
|
||||
/^[\],:{}\s]*$/.test(
|
||||
text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
|
||||
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
|
||||
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
||||
.replace(/(?:^|:|,)(?:\s*\[)+/g, '')
|
||||
)
|
||||
) {
|
||||
|
||||
// In the third stage we use the eval function to compile the text into a
|
||||
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
|
||||
|
271
js/util.js
@ -32,7 +32,6 @@ var SN = { // StatusNet
|
||||
MaxLength: 140,
|
||||
PatternUsername: /^[0-9a-zA-Z\-_.]*$/,
|
||||
HTTP20x30x: [200, 201, 202, 203, 204, 205, 206, 300, 301, 302, 303, 304, 305, 306, 307],
|
||||
NoticeFormMaster: null // to be cloned from the one at top
|
||||
},
|
||||
|
||||
/**
|
||||
@ -58,6 +57,9 @@ var SN = { // StatusNet
|
||||
}
|
||||
},
|
||||
|
||||
V: { // Variables
|
||||
},
|
||||
|
||||
/**
|
||||
* Map of localized message strings exported to script from the PHP
|
||||
* side via Action::getScriptMessages().
|
||||
@ -94,12 +96,12 @@ var SN = { // StatusNet
|
||||
* @access private
|
||||
*/
|
||||
FormNoticeEnhancements: function (form) {
|
||||
if (jQuery.data(form[0], 'ElementData') === undefined) {
|
||||
if ($.data(form[0], 'ElementData') === undefined) {
|
||||
var MaxLength = form.find('.count').text();
|
||||
if (MaxLength === undefined) {
|
||||
MaxLength = SN.C.I.MaxLength;
|
||||
}
|
||||
jQuery.data(form[0], 'ElementData', {MaxLength: MaxLength});
|
||||
$.data(form[0], 'ElementData', {MaxLength: MaxLength});
|
||||
|
||||
SN.U.Counter(form);
|
||||
|
||||
@ -122,7 +124,7 @@ var SN = { // StatusNet
|
||||
NDT.on('cut', delayedUpdate)
|
||||
.on('paste', delayedUpdate);
|
||||
} else {
|
||||
form.find('.count').text(jQuery.data(form[0], 'ElementData').MaxLength);
|
||||
form.find('.count').text($.data(form[0], 'ElementData').MaxLength);
|
||||
}
|
||||
},
|
||||
|
||||
@ -143,7 +145,7 @@ var SN = { // StatusNet
|
||||
Counter: function (form) {
|
||||
SN.C.I.FormNoticeCurrent = form;
|
||||
|
||||
var MaxLength = jQuery.data(form[0], 'ElementData').MaxLength;
|
||||
var MaxLength = $.data(form[0], 'ElementData').MaxLength;
|
||||
|
||||
if (MaxLength <= 0) {
|
||||
return;
|
||||
@ -217,6 +219,18 @@ var SN = { // StatusNet
|
||||
return url;
|
||||
},
|
||||
|
||||
FormNoticeUniqueID: function (form) {
|
||||
var oldId = form.attr('id');
|
||||
var newId = 'form_notice_' + Math.floor(Math.random()*999999999);
|
||||
var attrs = ['name', 'for', 'id'];
|
||||
for (var key in attrs) {
|
||||
form.find("[" + attrs[key] + "~='" + oldId + "']").each(function () {
|
||||
var newAttr = $(this).attr(attrs[key]).replace(oldId, newId);
|
||||
$(this).attr(attrs[key], newAttr);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Grabs form data and submits it asynchronously, with 'ajax=1'
|
||||
* parameter added to the rest.
|
||||
@ -375,7 +389,7 @@ var SN = { // StatusNet
|
||||
if ($('.' + SN.C.S.Error, response).length > 0) {
|
||||
form.append(document._importNode($('.' + SN.C.S.Error, response)[0], true));
|
||||
} else {
|
||||
if (parseInt(xhr.status) === 0 || jQuery.inArray(parseInt(xhr.status), SN.C.I.HTTP20x30x) >= 0) {
|
||||
if (parseInt(xhr.status) === 0 || $.inArray(parseInt(xhr.status), SN.C.I.HTTP20x30x) >= 0) {
|
||||
form
|
||||
.resetForm()
|
||||
.find('.attach-status').remove();
|
||||
@ -405,16 +419,14 @@ var SN = { // StatusNet
|
||||
if (replyItem.length > 0) {
|
||||
// If this is an inline reply, remove the form...
|
||||
var list = form.closest('.threaded-replies');
|
||||
var placeholder = list.find('.notice-reply-placeholder');
|
||||
replyItem.remove();
|
||||
|
||||
var id = $(notice).attr('id');
|
||||
if ($('#' + id).length == 0) {
|
||||
$(notice).insertBefore(placeholder);
|
||||
$(notice).insertBefore(replyItem);
|
||||
} // else Realtime came through before us...
|
||||
|
||||
// ...and show the placeholder form.
|
||||
placeholder.show();
|
||||
replyItem.remove();
|
||||
|
||||
} else if (notices.length > 0 && SN.U.belongsOnTimeline(notice)) {
|
||||
// Not a reply. If on our timeline, show it at the top!
|
||||
|
||||
@ -578,6 +590,44 @@ var SN = { // StatusNet
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup function -- DOES NOT trigger actions immediately.
|
||||
*
|
||||
* Sets up event handlers on all visible notice's option <a> elements
|
||||
* with the "popup" class so they behave as expected with AJAX.
|
||||
*
|
||||
* (without javascript the link goes to a page that expects you to verify
|
||||
* the action through a form)
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
NoticeOptionsAjax: function () {
|
||||
$(document).on('click', '.notice-options > a.popup', function (e) {
|
||||
e.preventDefault();
|
||||
var noticeEl = $(this).closest('.notice');
|
||||
$.ajax({
|
||||
url: $(this).attr('href'),
|
||||
data: {ajax: 1},
|
||||
success: function (data, textStatus, xhr) {
|
||||
SN.U.NoticeOptionPopup(data, noticeEl);
|
||||
},
|
||||
});
|
||||
return false;
|
||||
});
|
||||
},
|
||||
|
||||
NoticeOptionPopup: function (data, noticeEl) {
|
||||
title = $('head > title', data).text();
|
||||
body = $('body', data).html();
|
||||
dialog = $(body).dialog({
|
||||
height: "auto",
|
||||
width: "auto",
|
||||
modal: true,
|
||||
resizable: true,
|
||||
title: title,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup function -- DOES NOT trigger actions immediately.
|
||||
*
|
||||
@ -616,41 +666,18 @@ var SN = { // StatusNet
|
||||
NoticeInlineReplyTrigger: function (notice, initialText) {
|
||||
// Find the notice we're replying to...
|
||||
var id = $($('.notice_id', notice)[0]).text();
|
||||
var replyForm, placeholder;
|
||||
var replyForm;
|
||||
var parentNotice = notice;
|
||||
var stripForm = true; // strip a couple things out of reply forms that are inline
|
||||
|
||||
// Find the threaded replies view we'll be adding to...
|
||||
var list = notice.closest('.notices');
|
||||
if (list.closest('.old-school').length) {
|
||||
// We're replying to an old-school conversation thread;
|
||||
// use the old-style ping into the top form.
|
||||
SN.U.switchInputFormTab("status");
|
||||
replyForm = $('#input_form_status').find('form');
|
||||
stripForm = false;
|
||||
} else if (list.hasClass('threaded-replies')) {
|
||||
// We're replying to a reply; use reply form on the end of this list.
|
||||
// We'll add our form at the end of this; grab the root notice.
|
||||
parentNotice = list.closest('.notice');
|
||||
|
||||
// See if the form's already open...
|
||||
replyForm = $('.notice-reply-form', list);
|
||||
} else {
|
||||
// We're replying to a parent notice; pull its threaded list
|
||||
// and we'll add on the end of it. Will add if needed.
|
||||
list = $('ul.threaded-replies', notice);
|
||||
var list = notice.find('.threaded-replies');
|
||||
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);
|
||||
list = notice.closest('.threaded-replies');
|
||||
}
|
||||
}
|
||||
|
||||
// See if the form's already open...
|
||||
replyForm = $('.notice-reply-form', list);
|
||||
if (list.length == 0) {
|
||||
list = $('<ul class="notices threaded-replies xoxo"></ul>');
|
||||
notice.append(list);
|
||||
list = notice.find('.threaded-replies');
|
||||
}
|
||||
|
||||
var nextStep = function () {
|
||||
@ -663,6 +690,7 @@ var SN = { // StatusNet
|
||||
replyForm.find('label[for=notice_to]').hide();
|
||||
replyForm.find('label[for=notice_private]').hide();
|
||||
}
|
||||
replyItem.show();
|
||||
|
||||
// Set focus...
|
||||
var text = replyForm.find('textarea');
|
||||
@ -681,82 +709,64 @@ var SN = { // StatusNet
|
||||
text[0].setSelectionRange(len, len);
|
||||
}
|
||||
};
|
||||
if (replyForm.length > 0) {
|
||||
// Update the existing form...
|
||||
nextStep();
|
||||
} else {
|
||||
// Hide the placeholder...
|
||||
placeholder = list.find('li.notice-reply-placeholder').hide();
|
||||
|
||||
// Create the reply form entry at the end
|
||||
// Create the reply form entry
|
||||
var replyItem = $('li.notice-reply', list);
|
||||
if (replyItem.length == 0) {
|
||||
replyItem = $('<li class="notice-reply"></li>');
|
||||
}
|
||||
replyForm = replyItem.children('form');
|
||||
if (replyForm.length == 0) {
|
||||
// Let's try another trick to avoid fetching by URL
|
||||
var noticeForm = $('#input_form_status > form');
|
||||
if (noticeForm.length == 0) {
|
||||
// No notice form found on the page, so let's just
|
||||
// fetch a fresh copy of the notice form over AJAX.
|
||||
$.ajax({
|
||||
url: SN.V.urlNewNotice,
|
||||
data: {ajax: 1, inreplyto: id},
|
||||
success: function (data, textStatus, xhr) {
|
||||
var formEl = document._importNode($('form', data)[0], true);
|
||||
replyForm = $(formEl);
|
||||
replyItem.append(replyForm);
|
||||
list.append(replyItem);
|
||||
|
||||
var intermediateStep = function (formMaster) {
|
||||
var formEl = document._importNode(formMaster, true);
|
||||
replyItem.append(formEl);
|
||||
list.append(replyItem); // *after* the placeholder
|
||||
|
||||
var form = $(formEl);
|
||||
replyForm = form;
|
||||
SN.Init.NoticeFormSetup(form);
|
||||
|
||||
SN.Init.NoticeFormSetup(replyForm);
|
||||
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]);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
NoticeInlineReplyPlaceholder: function (notice) {
|
||||
var list = notice.find('ul.threaded-replies');
|
||||
if (list.length == 0) {
|
||||
list = $('<ul class="notices threaded-replies xoxo"></ul>');
|
||||
notice.append(list);
|
||||
list = notice.find('ul.threaded-replies');
|
||||
});
|
||||
// We do everything relevant in 'success' above
|
||||
return;
|
||||
}
|
||||
var placeholder = $('<li class="notice-reply-placeholder">' +
|
||||
'<input class="placeholder" />' +
|
||||
'</li>');
|
||||
placeholder.find('input')
|
||||
.val(SN.msg('reply_placeholder'));
|
||||
list.append(placeholder);
|
||||
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.
|
||||
nextStep();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup function -- DOES NOT apply immediately.
|
||||
*
|
||||
* Sets up event handlers for inline reply mini-form placeholders.
|
||||
* Uses 'on' rather than 'live' or 'bind', so applies to future as well as present items.
|
||||
*/
|
||||
NoticeInlineReplySetup: function () {
|
||||
$('li.notice-reply-placeholder input')
|
||||
.on('focus', function () {
|
||||
var notice = $(this).closest('li.notice');
|
||||
SN.U.NoticeInlineReplyTrigger(notice);
|
||||
return false;
|
||||
});
|
||||
// Expand conversation links
|
||||
$(document).on('click', 'li.notice-reply-comments a', function () {
|
||||
var url = $(this).attr('href');
|
||||
var area = $(this).closest('.threaded-replies');
|
||||
$.get(url, {ajax: 1}, function (data, textStatus, xhr) {
|
||||
$.ajax({
|
||||
url: url,
|
||||
data: {ajax: 1},
|
||||
success: function (data, textStatus, xhr) {
|
||||
var replies = $('.threaded-replies', data);
|
||||
if (replies.length) {
|
||||
area.replaceWith(document._importNode(replies[0], true));
|
||||
}
|
||||
},
|
||||
});
|
||||
return false;
|
||||
});
|
||||
@ -1043,7 +1053,7 @@ var SN = { // StatusNet
|
||||
|
||||
function removeNoticeDataGeo(error) {
|
||||
label
|
||||
.attr('title', jQuery.trim(label.text()))
|
||||
.attr('title', $.trim(label.text()))
|
||||
.removeClass('checked');
|
||||
|
||||
form.find('[name=lat]').val('');
|
||||
@ -1460,16 +1470,13 @@ var SN = { // StatusNet
|
||||
// Only close if there's been no edit.
|
||||
if (cur == '' || cur == textarea.data('initialText')) {
|
||||
var parentNotice = replyItem.closest('li.notice');
|
||||
replyItem.remove();
|
||||
replyItem.hide();
|
||||
parentNotice.find('li.notice-reply-placeholder').show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Infield labels for notice form inputs.
|
||||
$('.input_forms fieldset fieldset label').inFieldLabels({ fadeOpacity:0 });
|
||||
}
|
||||
},
|
||||
|
||||
@ -1481,13 +1488,15 @@ var SN = { // StatusNet
|
||||
* @param {jQuery} form
|
||||
*/
|
||||
NoticeFormSetup: function (form) {
|
||||
if (!form.data('NoticeFormSetup')) {
|
||||
if (form.data('NoticeFormSetup')) {
|
||||
return false;
|
||||
}
|
||||
SN.U.NoticeLocationAttach(form);
|
||||
SN.U.FormNoticeUniqueID(form);
|
||||
SN.U.FormNoticeXHR(form);
|
||||
SN.U.FormNoticeEnhancements(form);
|
||||
SN.U.NoticeDataAttach(form);
|
||||
form.data('NoticeFormSetup', true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1498,13 +1507,10 @@ var SN = { // StatusNet
|
||||
*/
|
||||
Notices: function () {
|
||||
if ($('body.user_in').length > 0) {
|
||||
var masterForm = $('.form_notice:first');
|
||||
if (masterForm.length > 0) {
|
||||
SN.C.I.NoticeFormMaster = document._importNode(masterForm[0], true);
|
||||
}
|
||||
SN.U.NoticeRepeat();
|
||||
SN.U.NoticeReply();
|
||||
SN.U.NoticeInlineReplySetup();
|
||||
SN.U.NoticeOptionsAjax();
|
||||
}
|
||||
|
||||
SN.U.NoticeAttachments();
|
||||
@ -1562,60 +1568,6 @@ var SN = { // StatusNet
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a people tag edit box is shown in the interface
|
||||
*
|
||||
* - loads the jQuery UI autocomplete plugin
|
||||
* - sets event handlers for tag completion
|
||||
*
|
||||
*/
|
||||
PeopletagAutocomplete: function (txtBox) {
|
||||
var split = function (val) {
|
||||
return val.split( /\s+/ );
|
||||
}
|
||||
var extractLast = function (term) {
|
||||
return split(term).pop();
|
||||
}
|
||||
|
||||
// don't navigate away from the field on tab when selecting an item
|
||||
txtBox.on( "keydown", function ( event ) {
|
||||
if ( event.keyCode === $.ui.keyCode.TAB &&
|
||||
$(this).data( "autocomplete" ).menu.active ) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}).autocomplete({
|
||||
minLength: 0,
|
||||
source: function (request, response) {
|
||||
// delegate back to autocomplete, but extract the last term
|
||||
response($.ui.autocomplete.filter(
|
||||
SN.C.PtagACData, extractLast(request.term)));
|
||||
},
|
||||
focus: function () {
|
||||
return false;
|
||||
},
|
||||
select: function (event, ui) {
|
||||
var terms = split(this.value);
|
||||
terms.pop();
|
||||
terms.push(ui.item.value);
|
||||
terms.push("");
|
||||
this.value = terms.join(" ");
|
||||
return false;
|
||||
}
|
||||
}).data('autocomplete')._renderItem = function (ul, item) {
|
||||
// FIXME: with jQuery UI you cannot have it highlight the match
|
||||
var _l = '<a class="ptag-ac-line-tag">' + item.tag
|
||||
+ ' <em class="privacy_mode">' + item.mode + '</em>'
|
||||
+ '<span class="freq">' + item.freq + '</span></a>'
|
||||
|
||||
return $("<li/>")
|
||||
.addClass('mode-' + item.mode)
|
||||
.addClass('ptag-ac-line')
|
||||
.data("item.autocomplete", item)
|
||||
.append(_l)
|
||||
.appendTo(ul);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Run setup for the ajax people tags editor
|
||||
*
|
||||
@ -1644,7 +1596,6 @@ var SN = { // StatusNet
|
||||
}
|
||||
|
||||
SN.C.PtagACData = data;
|
||||
SN.Init.PeopletagAutocomplete(form.find('#tags'));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -118,16 +118,18 @@ class Action extends HTMLOutputter // lawsuit
|
||||
common_config_set('db', 'database', $mirror);
|
||||
}
|
||||
|
||||
$status = $this->prepare($args);
|
||||
if ($status) {
|
||||
if (Event::handle('StartActionExecute', array($this, &$args))) {
|
||||
$prepared = $this->prepare($args);
|
||||
if ($prepared) {
|
||||
$this->handle($args);
|
||||
} else {
|
||||
common_debug('Prepare failed for Action.');
|
||||
}
|
||||
}
|
||||
|
||||
$this->flush();
|
||||
|
||||
Event::handle('EndActionExecute', array($status, $this));
|
||||
Event::handle('EndActionExecute', array($this));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,8 +158,8 @@ class Action extends HTMLOutputter // lawsuit
|
||||
$this->action = strtolower($this->trimmed('action'));
|
||||
|
||||
if ($this->ajax || $this->boolean('ajax')) {
|
||||
// check with StatusNet::isAjax()
|
||||
StatusNet::setAjax(true);
|
||||
// check with GNUsocial::isAjax()
|
||||
GNUsocial::setAjax(true);
|
||||
}
|
||||
|
||||
if ($this->needLogin) {
|
||||
@ -206,7 +208,7 @@ class Action extends HTMLOutputter // lawsuit
|
||||
*/
|
||||
function showPage()
|
||||
{
|
||||
if (StatusNet::isAjax()) {
|
||||
if (GNUsocial::isAjax()) {
|
||||
self::showAjax();
|
||||
return;
|
||||
}
|
||||
@ -326,7 +328,7 @@ class Action extends HTMLOutputter // lawsuit
|
||||
} else {
|
||||
// favicon.ico should be HTTPS if the rest of the page is
|
||||
$this->element('link', array('rel' => 'shortcut icon',
|
||||
'href' => common_path('favicon.ico', StatusNet::isHTTPS())));
|
||||
'href' => common_path('favicon.ico', GNUsocial::isHTTPS())));
|
||||
}
|
||||
|
||||
if (common_config('site', 'mobile')) {
|
||||
@ -415,8 +417,7 @@ class Action extends HTMLOutputter // lawsuit
|
||||
$this->script('extlib/jquery.form.js');
|
||||
$this->script('extlib/jquery-ui/jquery-ui.js');
|
||||
$this->script('extlib/jquery.cookie.js');
|
||||
$this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/extlib/json2.js', StatusNet::isHTTPS()).'"); }');
|
||||
$this->script('extlib/jquery.infieldlabel.js');
|
||||
$this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/extlib/json2.js', GNUsocial::isHTTPS()).'"); }');
|
||||
|
||||
Event::handle('EndShowJQueryScripts', array($this));
|
||||
}
|
||||
@ -430,6 +431,7 @@ class Action extends HTMLOutputter // lawsuit
|
||||
$this->inlineScript('var _peopletagAC = "' .
|
||||
common_local_url('peopletagautocomplete') . '";');
|
||||
$this->showScriptMessages();
|
||||
$this->showScriptVariables();
|
||||
// Anti-framing code to avoid clickjacking attacks in older browsers.
|
||||
// This will show a blank page if the page is being framed, which is
|
||||
// consistent with the behavior of the 'X-Frame-Options: SAMEORIGIN'
|
||||
@ -460,12 +462,6 @@ class Action extends HTMLOutputter // lawsuit
|
||||
// TRANS: Localized tooltip for '...' expansion button on overlong remote messages.
|
||||
$messages['showmore_tooltip'] = _m('TOOLTIP', 'Show more');
|
||||
|
||||
// TRANS: Inline reply form submit button: submits a reply comment.
|
||||
$messages['reply_submit'] = _m('BUTTON', 'Reply');
|
||||
|
||||
// TRANS: Placeholder text for inline reply form. Clicking in this box will turn it into a mini notice form.
|
||||
$messages['reply_placeholder'] = _m('Write a reply...');
|
||||
|
||||
$messages = array_merge($messages, $this->getScriptMessages());
|
||||
|
||||
Event::handle('EndScriptMessages', array($this, &$messages));
|
||||
@ -478,6 +474,19 @@ class Action extends HTMLOutputter // lawsuit
|
||||
return $messages;
|
||||
}
|
||||
|
||||
protected function showScriptVariables()
|
||||
{
|
||||
$vars = array();
|
||||
|
||||
if (Event::handle('StartScriptVariables', array($this, &$vars))) {
|
||||
$vars['urlNewNotice'] = common_local_url('newnotice');
|
||||
}
|
||||
if (!empty($vars)) {
|
||||
$this->inlineScript('SN.V = ' . json_encode($vars));
|
||||
}
|
||||
return $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the action will need localizable text strings, export them here like so:
|
||||
*
|
||||
@ -638,7 +647,7 @@ class Action extends HTMLOutputter // lawsuit
|
||||
$this->elementStart('a', array('class' => 'home bookmark',
|
||||
'href' => $url));
|
||||
|
||||
if (StatusNet::isHTTPS()) {
|
||||
if (GNUsocial::isHTTPS()) {
|
||||
$logoUrl = common_config('site', 'ssllogo');
|
||||
if (empty($logoUrl)) {
|
||||
// if logo is an uploaded file, try to fall back to HTTPS file URL
|
||||
@ -1144,7 +1153,7 @@ class Action extends HTMLOutputter // lawsuit
|
||||
$image = common_config('license', 'image');
|
||||
$sslimage = common_config('license', 'sslimage');
|
||||
|
||||
if (StatusNet::isHTTPS()) {
|
||||
if (GNUsocial::isHTTPS()) {
|
||||
if (!empty($sslimage)) {
|
||||
$url = $sslimage;
|
||||
} else if (preg_match('#^http://i.creativecommons.org/#', $image)) {
|
||||
@ -1354,6 +1363,19 @@ class Action extends HTMLOutputter // lawsuit
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a cheap hack to avoid a bug in DB_DataObject
|
||||
* where '' is non-type-aware compared to 0, which means it
|
||||
* will always be true for values like false and 0 too...
|
||||
*
|
||||
* Upstream bug is::
|
||||
* https://pear.php.net/bugs/bug.php?id=20291
|
||||
*/
|
||||
function booleanintstring($key, $def=false)
|
||||
{
|
||||
return $this->boolean($key, $def) ? '1' : '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Integer value of an argument
|
||||
*
|
||||
|
@ -160,10 +160,11 @@ abstract class ActivityHandlerPlugin extends Plugin
|
||||
* @fixme are there any standard options?
|
||||
*
|
||||
* @param Activity $activity
|
||||
* @param Profile $actor
|
||||
* @param Notice $stored The notice in our database for this certain object
|
||||
* @param array $options=array()
|
||||
*
|
||||
* @return Notice the resulting notice
|
||||
* @return object If the verb handling plugin creates an object, it can be returned here (otherwise true)
|
||||
* @throws exception On any error.
|
||||
*/
|
||||
protected function saveObjectFromActivity(Activity $activity, Notice $stored, array $options=array())
|
||||
{
|
||||
@ -174,7 +175,7 @@ abstract class ActivityHandlerPlugin extends Plugin
|
||||
* This usually gets called from Notice::saveActivity after a Notice object has been created,
|
||||
* so it contains a proper id and a uri for the object to be saved.
|
||||
*/
|
||||
public function onStoreActivityObject(Activity $act, Notice $stored, array $options=array(), &$object) {
|
||||
public function onStoreActivityObject(Activity $act, Notice $stored, array $options, &$object) {
|
||||
// $this->oldSaveNew is there during a migration period of plugins, to start using
|
||||
// Notice::saveActivity instead of Notice::saveNew
|
||||
if (!$this->isMyActivity($act) || isset($this->oldSaveNew)) {
|
||||
@ -182,7 +183,13 @@ abstract class ActivityHandlerPlugin extends Plugin
|
||||
}
|
||||
$object = $this->saveObjectFromActivity($act, $stored, $options);
|
||||
try {
|
||||
// In the future we probably want to use something like ActivityVerb_DataObject for the kind
|
||||
// of objects which are returned from saveObjectFromActivity.
|
||||
if ($object instanceof Managed_DataObject) {
|
||||
// If the verb handling plugin figured out some more attention URIs, add them here to the
|
||||
// original activity. This is only done if a separate object is actually needed to be saved.
|
||||
$act->context->attention = array_merge($act->context->attention, $object->getAttentionArray());
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
common_debug('WARNING: Could not get attention list from object '.get_class($object).'!');
|
||||
}
|
||||
@ -595,7 +602,6 @@ abstract class ActivityHandlerPlugin extends Plugin
|
||||
$nli->showNoticeSource();
|
||||
$nli->showNoticeLocation();
|
||||
$nli->showPermalink();
|
||||
$nli->showRepeat();
|
||||
|
||||
$nli->showNoticeOptions();
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ class ActivityImporter extends QueueHandler
|
||||
|
||||
// XXX: don't do this for untrusted input!
|
||||
|
||||
Subscription::start($otherProfile, $profile);
|
||||
Subscription::ensureStart($otherProfile, $profile);
|
||||
} else if (empty($activity->actor)
|
||||
|| $activity->actor->id == $author->id) {
|
||||
|
||||
@ -123,7 +123,7 @@ class ActivityImporter extends QueueHandler
|
||||
throw new ClientException(_('Unknown profile.'));
|
||||
}
|
||||
|
||||
Subscription::start($profile, $otherProfile);
|
||||
Subscription::ensureStart($profile, $otherProfile);
|
||||
} else {
|
||||
// TRANS: Client exception thrown when trying to import an event not related to the importing user.
|
||||
throw new Exception(_('This activity seems unrelated to our user.'));
|
||||
@ -213,7 +213,7 @@ class ActivityImporter extends QueueHandler
|
||||
|
||||
// Get (safe!) HTML and text versions of the content
|
||||
|
||||
$rendered = $this->purify($sourceContent);
|
||||
$rendered = common_purify($sourceContent);
|
||||
$content = common_strip_html($rendered);
|
||||
|
||||
$shortened = $user->shortenLinks($content);
|
||||
@ -338,15 +338,4 @@ class ActivityImporter extends QueueHandler
|
||||
|
||||
return array($groups, $replies);
|
||||
}
|
||||
|
||||
|
||||
function purify($content)
|
||||
{
|
||||
require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
|
||||
|
||||
$config = array('safe' => 1,
|
||||
'deny_attribute' => 'id,style,on*');
|
||||
|
||||
return htmLawed($content, $config);
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ class ActivityMover extends QueueHandler
|
||||
"Changing sub to {$act->objects[0]->id}".
|
||||
"by {$act->actor->id} to {$remote->nickname}.");
|
||||
$otherProfile = $otherUser->getProfile();
|
||||
Subscription::start($otherProfile, $remote);
|
||||
Subscription::ensureStart($otherProfile, $remote);
|
||||
Subscription::cancel($otherProfile, $user->getProfile());
|
||||
} else {
|
||||
$this->log(LOG_NOTICE,
|
||||
|
@ -512,11 +512,11 @@ class ActivityObject
|
||||
|
||||
switch (self::canonicalType($object->type)) {
|
||||
case 'image':
|
||||
$object->largerImage = $file->url;
|
||||
$object->largerImage = $file->getUrl();
|
||||
break;
|
||||
case 'video':
|
||||
case 'audio':
|
||||
$object->stream = $file->url;
|
||||
$object->stream = $file->getUrl();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -861,7 +861,7 @@ class ActivityObject
|
||||
if (is_string($this->thumbnail)) {
|
||||
$object['image'] = array('url' => $this->thumbnail);
|
||||
} else {
|
||||
$object['image'] = array('url' => $this->thumbnail->url);
|
||||
$object['image'] = array('url' => $this->thumbnail->getUrl());
|
||||
if ($this->thumbnail->width) {
|
||||
$object['image']['width'] = $this->thumbnail->width;
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ class ActivityVerb
|
||||
const SHARE = 'http://activitystrea.ms/schema/1.0/share';
|
||||
const SAVE = 'http://activitystrea.ms/schema/1.0/save';
|
||||
const FAVORITE = 'http://activitystrea.ms/schema/1.0/favorite';
|
||||
const LIKE = 'http://activitystrea.ms/schema/1.0/like'; // This is a synonym of favorite
|
||||
const PLAY = 'http://activitystrea.ms/schema/1.0/play';
|
||||
const FOLLOW = 'http://activitystrea.ms/schema/1.0/follow';
|
||||
const FRIEND = 'http://activitystrea.ms/schema/1.0/make-friend';
|
||||
@ -56,7 +57,8 @@ class ActivityVerb
|
||||
|
||||
// Custom OStatus verbs for the flipside until they're standardized
|
||||
const DELETE = 'http://ostatus.org/schema/1.0/unfollow';
|
||||
const UNFAVORITE = 'http://ostatus.org/schema/1.0/unfavorite';
|
||||
const UNFAVORITE = 'http://activitystrea.ms/schema/1.0/unfavorite';
|
||||
const UNLIKE = 'http://activitystrea.ms/schema/1.0/unlike'; // This is a synonym of unfavorite
|
||||
const UNFOLLOW = 'http://ostatus.org/schema/1.0/unfollow';
|
||||
const LEAVE = 'http://ostatus.org/schema/1.0/leave';
|
||||
const UNTAG = 'http://ostatus.org/schema/1.0/untag';
|
||||
|
@ -59,24 +59,8 @@ class AdminPanelNav extends Menu
|
||||
$nickname = $user->nickname;
|
||||
$name = $user->getProfile()->getBestName();
|
||||
|
||||
// Stub section w/ home link
|
||||
$this->action->elementStart('ul');
|
||||
$this->action->elementStart('li');
|
||||
// TRANS: Header in administrator navigation panel.
|
||||
$this->action->element('h3', null, _m('HEADER','Home'));
|
||||
$this->action->elementStart('ul', 'nav');
|
||||
$this->out->menuItem(common_local_url('all', array('nickname' =>
|
||||
$nickname)),
|
||||
// TRANS: Menu item in administrator navigation panel.
|
||||
_m('MENU','Home'),
|
||||
// TRANS: Menu item title in administrator navigation panel.
|
||||
// TRANS: %s is a username.
|
||||
sprintf(_('%s and friends'), $name),
|
||||
$this->action == 'all', 'nav_timeline_personal');
|
||||
|
||||
$this->action->elementEnd('ul');
|
||||
$this->action->elementEnd('li');
|
||||
$this->action->elementEnd('ul');
|
||||
$stub = new HomeStubNav($this->action);
|
||||
$this->submenu(_m('MENU','Home'), $stub);
|
||||
|
||||
$this->action->elementStart('ul');
|
||||
$this->action->elementStart('li');
|
||||
|
@ -144,7 +144,7 @@ class ApiAction extends Action
|
||||
*/
|
||||
protected function prepare(array $args=array())
|
||||
{
|
||||
StatusNet::setApi(true); // reduce exception reports to aid in debugging
|
||||
GNUsocial::setApi(true); // reduce exception reports to aid in debugging
|
||||
parent::prepare($args);
|
||||
|
||||
$this->format = $this->arg('format');
|
||||
@ -159,7 +159,7 @@ class ApiAction extends Action
|
||||
$this->limit = $this->count + 1;
|
||||
|
||||
if ($this->arg('since')) {
|
||||
header('X-StatusNet-Warning: since parameter is disabled; use since_id');
|
||||
header('X-GNUsocial-Warning: since parameter is disabled; use since_id');
|
||||
}
|
||||
|
||||
$this->source = $this->trimmed('source');
|
||||
@ -264,22 +264,20 @@ class ApiAction extends Action
|
||||
$twitter_user['statuses_count'] = $profile->noticeCount();
|
||||
|
||||
// Is the requesting user following this user?
|
||||
// These values might actually also mean "unknown". Ambiguity issues?
|
||||
$twitter_user['following'] = false;
|
||||
$twitter_user['statusnet_blocking'] = false;
|
||||
$twitter_user['notifications'] = false;
|
||||
|
||||
if (isset($this->auth_user)) {
|
||||
|
||||
$twitter_user['following'] = $this->auth_user->isSubscribed($profile);
|
||||
$twitter_user['statusnet_blocking'] = $this->auth_user->hasBlocked($profile);
|
||||
|
||||
if ($this->scoped instanceof Profile) {
|
||||
try {
|
||||
$sub = Subscription::getSubscription($this->scoped, $profile);
|
||||
// Notifications on?
|
||||
$sub = Subscription::pkeyGet(array('subscriber' =>
|
||||
$this->auth_user->id,
|
||||
'subscribed' => $profile->id));
|
||||
|
||||
if ($sub) {
|
||||
$twitter_user['following'] = true;
|
||||
$twitter_user['statusnet_blocking'] = $this->scoped->hasBlocked($profile);
|
||||
$twitter_user['notifications'] = ($sub->jabber || $sub->sms);
|
||||
} catch (NoResultException $e) {
|
||||
// well, the values are already false...
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,6 +303,7 @@ class ApiAction extends Action
|
||||
{
|
||||
$base = $this->twitterSimpleStatusArray($notice, $include_user);
|
||||
|
||||
// FIXME: MOVE TO SHARE PLUGIN
|
||||
if (!empty($notice->repeat_of)) {
|
||||
$original = Notice::getKV('id', $notice->repeat_of);
|
||||
if ($original instanceof Notice) {
|
||||
@ -376,12 +375,6 @@ class ApiAction extends Action
|
||||
$twitter_status['geo'] = null;
|
||||
}
|
||||
|
||||
if (!is_null($this->scoped)) {
|
||||
$twitter_status['repeated'] = $this->scoped->hasRepeated($notice);
|
||||
} else {
|
||||
$twitter_status['repeated'] = false;
|
||||
}
|
||||
|
||||
// Enclosures
|
||||
$attachments = $notice->attachments();
|
||||
|
||||
@ -430,11 +423,11 @@ class ApiAction extends Action
|
||||
$twitter_group['nickname'] = $group->nickname;
|
||||
$twitter_group['fullname'] = $group->fullname;
|
||||
|
||||
if (isset($this->auth_user)) {
|
||||
$twitter_group['member'] = $this->auth_user->isMember($group);
|
||||
if ($this->scoped instanceof Profile) {
|
||||
$twitter_group['member'] = $this->scoped->isMember($group);
|
||||
$twitter_group['blocked'] = Group_block::isBlocked(
|
||||
$group,
|
||||
$this->auth_user->getProfile()
|
||||
$this->scoped
|
||||
);
|
||||
}
|
||||
|
||||
@ -485,8 +478,8 @@ class ApiAction extends Action
|
||||
$twitter_list['member_count'] = $list->taggedCount();
|
||||
$twitter_list['uri'] = $list->getUri();
|
||||
|
||||
if (isset($this->auth_user)) {
|
||||
$twitter_list['following'] = $list->hasSubscriber($this->auth_user);
|
||||
if ($this->scoped instanceof Profile) {
|
||||
$twitter_list['following'] = $list->hasSubscriber($this->scoped);
|
||||
} else {
|
||||
$twitter_list['following'] = false;
|
||||
}
|
||||
@ -575,37 +568,30 @@ class ApiAction extends Action
|
||||
$relationship = array();
|
||||
|
||||
$relationship['source'] =
|
||||
$this->relationshipDetailsArray($source, $target);
|
||||
$this->relationshipDetailsArray($source->getProfile(), $target->getProfile());
|
||||
$relationship['target'] =
|
||||
$this->relationshipDetailsArray($target, $source);
|
||||
$this->relationshipDetailsArray($target->getProfile(), $source->getProfile());
|
||||
|
||||
return array('relationship' => $relationship);
|
||||
}
|
||||
|
||||
function relationshipDetailsArray($source, $target)
|
||||
function relationshipDetailsArray(Profile $source, Profile $target)
|
||||
{
|
||||
$details = array();
|
||||
|
||||
$source_profile = $source->getProfile();
|
||||
$target_profile = $target->getProfile();
|
||||
$details['screen_name'] = $source->getNickname();
|
||||
$details['followed_by'] = $target->isSubscribed($source);
|
||||
|
||||
$details['screen_name'] = $source->nickname;
|
||||
$details['followed_by'] = $target->isSubscribed($source_profile);
|
||||
$details['following'] = $source->isSubscribed($target_profile);
|
||||
|
||||
$notifications = false;
|
||||
|
||||
if ($source->isSubscribed($target_profile)) {
|
||||
$sub = Subscription::pkeyGet(array('subscriber' =>
|
||||
$source->id, 'subscribed' => $target->id));
|
||||
|
||||
if (!empty($sub)) {
|
||||
$notifications = ($sub->jabber || $sub->sms);
|
||||
}
|
||||
try {
|
||||
$sub = Subscription::getSubscription($source, $target);
|
||||
$details['following'] = true;
|
||||
$details['notifications_enabled'] = ($sub->jabber || $sub->sms);
|
||||
} catch (NoResultException $e) {
|
||||
$details['following'] = false;
|
||||
$details['notifications_enabled'] = false;
|
||||
}
|
||||
|
||||
$details['notifications_enabled'] = $notifications;
|
||||
$details['blocking'] = $source->hasBlocked($target_profile);
|
||||
$details['blocking'] = $source->hasBlocked($target);
|
||||
$details['id'] = intval($source->id);
|
||||
|
||||
return $details;
|
||||
@ -655,6 +641,7 @@ class ApiAction extends Action
|
||||
$this->showGeoXML($value);
|
||||
break;
|
||||
case 'retweeted_status':
|
||||
// FIXME: MOVE TO SHARE PLUGIN
|
||||
$this->showTwitterXmlStatus($value, 'retweeted_status');
|
||||
break;
|
||||
default:
|
||||
@ -788,7 +775,7 @@ class ApiAction extends Action
|
||||
function showSingleAtomStatus($notice)
|
||||
{
|
||||
header('Content-Type: application/atom+xml; charset=utf-8');
|
||||
print $notice->asAtomEntry(true, true, true, $this->auth_user->getProfile());
|
||||
print $notice->asAtomEntry(true, true, true, $this->scoped);
|
||||
}
|
||||
|
||||
function show_single_json_status($notice)
|
||||
@ -1352,7 +1339,7 @@ class ApiAction extends Action
|
||||
return User::getKV('nickname', $nickname);
|
||||
} else {
|
||||
// Fall back to trying the currently authenticated user
|
||||
return $this->auth_user;
|
||||
return $this->scoped->getUser();
|
||||
}
|
||||
|
||||
} else if (self::is_decimal($id)) {
|
||||
@ -1448,7 +1435,7 @@ class ApiAction extends Action
|
||||
}
|
||||
|
||||
if (!empty($list) && $list->private) {
|
||||
if ($this->auth_user->id == $list->tagger) {
|
||||
if ($this->scoped->id == $list->tagger) {
|
||||
return $list;
|
||||
}
|
||||
} else {
|
||||
@ -1516,6 +1503,11 @@ class ApiAction extends Action
|
||||
$aargs['id'] = $id;
|
||||
}
|
||||
|
||||
$user = $this->arg('user');
|
||||
if (!empty($user)) {
|
||||
$aargs['user'] = $user;
|
||||
}
|
||||
|
||||
$tag = $this->arg('tag');
|
||||
if (!empty($tag)) {
|
||||
$aargs['tag'] = $tag;
|
||||
|