diff --git a/EVENTS.txt b/EVENTS.txt
index 8f094d1643..cfba97403b 100644
--- a/EVENTS.txt
+++ b/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
diff --git a/INSTALL b/INSTALL
index bddeb41b18..aad21756fe 100644
--- a/INSTALL
+++ b/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 .
+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 .
-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
+the Web directory. To backup the database use 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.
diff --git a/README.md b/README.md
index 714195d240..556ac07130 100644
--- a/README.md
+++ b/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, )
- Many improvements to ease adoption of the Qvitter front-end
- 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
diff --git a/UPGRADE b/UPGRADE
index 10758c52e4..d2cd365e55 100644
--- a/UPGRADE
+++ b/UPGRADE
@@ -1,99 +1,98 @@
Upgrading
=========
-StatusNet 1.1.1 to GNU social
------------------------------
+GNU social 1.1.x to GNU social 1.2.x
+------------------------------------
+
+If you are tracking the GNU social git repository, we currently recommend
+using the "master" branch (or nightly if you want to use latest features)
+and follow this procedure:
+
+0. Backup your data. The StatusNet upgrade discussions below have some
+ guidelines to back up the database and files (mysqldump and rsync).
+
+1. Stop your queue daemons (you can run this command even if you do not
+ use the queue daemons):
+ $ bash scripts/stopdaemons.sh
+
+2. Run the command to fetch the latest sourcecode:
+ $ git pull
+
+ If you are not using git we recommend following the instructions below
+ for upgrading "StatusNet 1.1.x to GNU social 1.2.x" as they are similar.
+
+3. Run the upgrade script:
+ $ php scripts/upgrade.php
+
+ The upgrade script will likely take a long time because it will
+ upgrade the tables to another character encoding and make other
+ automated upgrades. Make sure it ends without errors. If you get
+ errors, create a new task on https://bugz.foocorp.net/
+
+4. Start your queue daemons again (you can run this command even if you
+ do not use the queue daemons):
+ $ bash scripts/startdaemons.sh
+
+5. Report any issues at https://bugz.foocorp.net/ (tag GNU social)
+
+If you are using ssh keys to log in to your server, you can make this
+procedure pretty painless (assuming you have automated backups already).
+Make sure you "cd" into the correct directory (in this case "htdocs")
+and use the correct login@hostname combo:
+ $ ssh social@domain.example 'cd htdocs
+ && bash scripts/stopdaemons.sh
+ && git pull
+ && time php scripts/upgrade.php
+ && bash scripts/startdaemons.sh'
+
+StatusNet 1.1.x to GNU social 1.2.x
+-----------------------------------
We cannot support migrating from any other version of StatusNet than
1.1.1. If you are running a StatusNet version lower than this, please
follow the upgrade procedures for each respective StatusNet version.
-You are now running StatusNet 1.1.1 and want to migrate to GNU social.
-Beware there may be changes in minimum required version of PHP and the
-modules used, so double-check the INSTALL file's requirements list.
+You are now running StatusNet 1.1.1 and want to migrate to GNU social
+1.2.x. Beware there may be changes in minimum required version of PHP
+and the modules required, so review the INSTALL file (php5-intl is a
+newly added dependency for example).
-Before you begin: Make backups. Always make backups. Of your entire
+* Before you begin: Make backups. Always make backups. Of your entire
directory structure and the database too. All tables. All data. Alles.
- 0. Stop your queue daemons 'php scripts/stopdaemon.php' should do it.
- Not everyone runs queue daemons, but the above command won't hurt.
+0. Make a backup of everything. To backup the database, you can use a
+variant of this command (you will be prompted for the database password):
+ $ mysqldump -u dbuser -p dbname > social-backup.sql
- 1. Unpack your GNU social code to a fresh directory.
-
- 2. Synchronize your local files to the GNU social directory. These
- will be the local files such as avatars, config and files:
+1. Stop your queue daemons 'bash scripts/stopdaemons.sh' should do it.
+ Not everyone runs queue daemons, but the above command won't hurt.
- avatar/*
- background/*
- file/*
- local/*
- .htaccess
- config.php
+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
- 3. 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. Start your queue daemons: 'php scripts/startdaemons.php'
-
- 6. Report any issues at https://bugz.foocorp.net/ (tag GNU social)
+3. Synchronize your local files to the GNU social directory. These
+ will be the local files such as avatars, config and files:
+ avatar/*
+ background/*
+ file/*
+ local/*
+ .htaccess
+ config.php
-Legacy StatusNet instructions
------------------------------
+ This command will point you in the right direction on how to do it:
+ $ rsync -avP statusnet/{.htaccess,avatar,background,file,local,config.php} gnusocial/
-These instructions are here for historical and perhaps informational
-purposes.
+4. Replace your old StatusNet directory with the new GNU social
+ directory in your webserver root.
-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.
+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/
-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.
+6. Start your queue daemons: 'bash scripts/startdaemons.sh'
- 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)
diff --git a/actions/apiaccountregister.php b/actions/apiaccountregister.php
index b5481df1cb..0d018007cd 100644
--- a/actions/apiaccountregister.php
+++ b/actions/apiaccountregister.php
@@ -1,5 +1,4 @@
clientError(_('Passwords do not match.'), 400);
} else {
-
- // annoy spammers
- sleep(7);
-
- if ($user = User::register(array('nickname' => $nickname,
- 'password' => $password,
- 'email' => $email,
- 'fullname' => $fullname,
- 'homepage' => $homepage,
- 'bio' => $bio,
- 'location' => $location,
- 'code' => $this->code))) {
- if (!$user instanceof User) {
- // TRANS: Form validation error displayed when trying to register with an invalid username or password.
- $this->clientError(_('Invalid username or password.'), 400);
- }
- Event::handle('EndRegistrationTry', array($this));
+ // annoy spammers
+ sleep(7);
- $this->initDocument('json');
- $this->showJsonObjects($this->twitterUserArray($user->getProfile()));
- $this->endDocument('json');
+ try {
+ $user = User::register(array('nickname' => $nickname,
+ 'password' => $password,
+ 'email' => $email,
+ 'fullname' => $fullname,
+ 'homepage' => $homepage,
+ 'bio' => $bio,
+ 'location' => $location,
+ 'code' => $this->code));
+ Event::handle('EndRegistrationTry', array($this));
- } else {
- // TRANS: Form validation error displayed when trying to register with an invalid username or password.
- $this->clientError(_('Invalid username or password.'), 400);
- }
- }
+ $this->initDocument('json');
+ $this->showJsonObjects($this->twitterUserArray($user->getProfile()));
+ $this->endDocument('json');
+
+ } catch (Exception $e) {
+ $this->clientError($e->getMessage(), 400);
+ }
+ }
}
/**
diff --git a/actions/apifriendshipscreate.php b/actions/apifriendshipscreate.php
index 873883c651..3997a8b51c 100644
--- a/actions/apifriendshipscreate.php
+++ b/actions/apifriendshipscreate.php
@@ -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);
diff --git a/actions/apifriendshipsdestroy.php b/actions/apifriendshipsdestroy.php
index 5f7c85e0af..5c8036bc45 100644
--- a/actions/apifriendshipsdestroy.php
+++ b/actions/apifriendshipsdestroy.php
@@ -29,9 +29,7 @@
* @link http://status.net/
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Allows the authenticating users to unfollow (unsubscribe) the user specified in
@@ -48,7 +46,9 @@ if (!defined('STATUSNET')) {
*/
class ApiFriendshipsDestroyAction extends ApiAuthAction
{
- var $other = null;
+ protected $needPost = true;
+
+ protected $other = null;
/**
* Take arguments for running
@@ -58,12 +58,11 @@ class ApiFriendshipsDestroyAction extends ApiAuthAction
* @return boolean success flag
*
*/
- function prepare($args)
+ protected function prepare(array $args=array())
{
parent::prepare($args);
- $this->user = $this->auth_user;
- $this->other = $this->getTargetProfile($this->arg('id'));
+ $this->other = $this->getTargetProfile($this->arg('id'));
return true;
}
@@ -73,58 +72,40 @@ class ApiFriendshipsDestroyAction extends ApiAuthAction
*
* Check the format and show the user info
*
- * @param array $args $_REQUEST data (unused)
- *
* @return void
*/
- function handle($args)
+ protected function handle()
{
- parent::handle($args);
-
- if ($_SERVER['REQUEST_METHOD'] != 'POST') {
- $this->clientError(
- // TRANS: Client error. POST is a HTTP command. It should not be translated.
- _('This method requires a POST.'),
- 400,
- $this->format
- );
- return;
- }
+ parent::handle();
if (!in_array($this->format, array('xml', 'json'))) {
$this->clientError(
// TRANS: Client error displayed when coming across a non-supported API method.
_('API method not found.'),
- 404,
- $this->format
+ 404
);
- return;
}
- if (empty($this->other)) {
+ if (!$this->other instanceof Profile) {
$this->clientError(
// TRANS: Client error displayed when trying to unfollow a user that cannot be found.
_('Could not unfollow user: User not found.'),
- 403,
- $this->format
+ 403
);
- return;
}
// Don't allow unsubscribing from yourself!
- if ($this->user->id == $this->other->id) {
+ if ($this->scoped->id == $this->other->id) {
$this->clientError(
// TRANS: Client error displayed when trying to unfollow self.
_("You cannot unfollow yourself."),
- 403,
- $this->format
+ 403
);
- return;
}
// throws an exception on error
- Subscription::cancel($this->user->getProfile(), $this->other);
+ Subscription::cancel($this->scoped, $this->other);
$this->initDocument($this->format);
$this->showProfile($this->other, $this->format);
diff --git a/actions/apifriendshipsexists.php b/actions/apifriendshipsexists.php
index 1f76e00b1a..4bb771292d 100644
--- a/actions/apifriendshipsexists.php
+++ b/actions/apifriendshipsexists.php
@@ -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);
diff --git a/actions/apifriendshipsshow.php b/actions/apifriendshipsshow.php
index 5810e14874..c08e984b9a 100644
--- a/actions/apifriendshipsshow.php
+++ b/actions/apifriendshipsshow.php
@@ -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.
diff --git a/actions/apitimelineuser.php b/actions/apitimelineuser.php
index 26c960fa04..e8c58e6e8b 100644
--- a/actions/apitimelineuser.php
+++ b/actions/apitimelineuser.php
@@ -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);
- }
}
diff --git a/actions/atompubsubscriptionfeed.php b/actions/atompubsubscriptionfeed.php
index 413277a5d3..6a483b95c0 100644
--- a/actions/atompubsubscriptionfeed.php
+++ b/actions/atompubsubscriptionfeed.php
@@ -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));
diff --git a/actions/attachment_thumbnail.php b/actions/attachment_thumbnail.php
index c85029dcf6..e8f89ffc36 100644
--- a/actions/attachment_thumbnail.php
+++ b/actions/attachment_thumbnail.php
@@ -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));
}
}
diff --git a/actions/avatarsettings.php b/actions/avatarsettings.php
index 7373e77047..1f31cbdafe 100644
--- a/actions/avatarsettings.php
+++ b/actions/avatarsettings.php
@@ -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']);
diff --git a/actions/cancelsubscription.php b/actions/cancelsubscription.php
index 35e775ab1d..16654ea020 100644
--- a/actions/cancelsubscription.php
+++ b/actions/cancelsubscription.php
@@ -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);
}
}
diff --git a/actions/doc.php b/actions/doc.php
index 6db3830ce1..85d35d8fcf 100644
--- a/actions/doc.php
+++ b/actions/doc.php
@@ -174,11 +174,15 @@ class DocNav extends Menu
{
function show()
{
- $stub = new HomeStubNav($this->action);
- $this->submenu(_m('MENU','Home'), $stub);
+ if (Event::handle('StartDocNav', array($this))) {
+ $stub = new HomeStubNav($this->action);
+ $this->submenu(_m('MENU','Home'), $stub);
- $docs = new DocListNav($this->action);
- $this->submenu(_m('MENU','Docs'), $docs);
+ $docs = new DocListNav($this->action);
+ $this->submenu(_m('MENU','Docs'), $docs);
+
+ Event::handle('EndDocNav', array($this));
+ }
}
}
diff --git a/actions/emailsettings.php b/actions/emailsettings.php
index 0c2033d821..dfdbe1bad0 100644
--- a/actions/emailsettings.php
+++ b/actions/emailsettings.php
@@ -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.'));
}
diff --git a/actions/grouplogo.php b/actions/grouplogo.php
index 10fd33088d..0d9c135785 100644
--- a/actions/grouplogo.php
+++ b/actions/grouplogo.php
@@ -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';
diff --git a/actions/invite.php b/actions/invite.php
index f99dd4d783..89b7e83bf6 100644
--- a/actions/invite.php
+++ b/actions/invite.php
@@ -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
diff --git a/actions/login.php b/actions/login.php
index 16016f29ec..f9fe71ed40 100644
--- a/actions/login.php
+++ b/actions/login.php
@@ -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()) {
diff --git a/actions/newapplication.php b/actions/newapplication.php
index ad71aaad0a..37bede0d72 100644
--- a/actions/newapplication.php
+++ b/actions/newapplication.php
@@ -28,9 +28,7 @@
* @link http://status.net/
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Add a new application
@@ -51,10 +49,8 @@ class NewApplicationAction extends FormAction
return _('New application');
}
- protected function handlePost()
+ protected function doPost()
{
- parent::handlePost();
-
if ($this->arg('cancel')) {
common_redirect(common_local_url('oauthappssettings'), 303);
} elseif ($this->arg('save')) {
@@ -65,27 +61,15 @@ class NewApplicationAction extends FormAction
$this->clientError(_('Unexpected form submission.'));
}
- function showForm($msg=null)
+ protected function getForm()
{
- $this->msg = $msg;
- $this->showPage();
+ return new ApplicationEditForm($this);
}
- function showContent()
+ protected function getInstructions()
{
- $form = new ApplicationEditForm($this);
- $form->show();
- }
-
- function showPageNotice()
- {
- if ($this->msg) {
- $this->element('p', 'error', $this->msg);
- } else {
- $this->element('p', 'instructions',
- // TRANS: Form instructions for registering a new application.
- _('Use this form to register a new application.'));
- }
+ // TRANS: Form instructions for registering a new application.
+ return _('Use this form to register a new application.');
}
private function trySave()
@@ -181,6 +165,7 @@ class NewApplicationAction extends FormAction
if (!$result) {
common_log_db_error($consumer, 'INSERT', __FILE__);
+ $app->query('ROLLBACK');
// TRANS: Server error displayed when an application could not be registered in the database through the "New application" form.
$this->serverError(_('Could not create application.'));
}
@@ -191,9 +176,9 @@ class NewApplicationAction extends FormAction
if (!$this->app_id) {
common_log_db_error($app, 'INSERT', __FILE__);
+ $app->query('ROLLBACK');
// TRANS: Server error displayed when an application could not be registered in the database through the "New application" form.
$this->serverError(_('Could not create application.'));
- $app->query('ROLLBACK');
}
try {
diff --git a/actions/newgroup.php b/actions/newgroup.php
index 07a302ef45..ef8346c03e 100644
--- a/actions/newgroup.php
+++ b/actions/newgroup.php
@@ -29,9 +29,7 @@
* @link http://status.net/
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Add a new group
@@ -48,6 +46,8 @@ class NewgroupAction extends FormAction
{
protected $group;
+ protected $form = 'GroupEdit';
+
function getGroup() {
return $this->group;
}
@@ -58,39 +58,23 @@ class NewgroupAction extends FormAction
return _('New group');
}
- /**
- * Prepare to run
- */
- protected function prepare(array $args=array())
+ protected function doPreparation()
{
- parent::prepare($args);
-
// $this->scoped is the current user profile
if (!$this->scoped->hasRight(Right::CREATEGROUP)) {
// TRANS: Client exception thrown when a user tries to create a group while banned.
$this->clientError(_('You are not allowed to create groups on this site.'), 403);
}
-
- return true;
}
- public function showContent()
+ protected function getInstructions()
{
- $form = new GroupEditForm($this);
- $form->show();
+ // TRANS: Form instructions for group create form.
+ return _('Use this form to create a new group.');
}
- public function showInstructions()
+ protected function doPost()
{
- $this->element('p', 'instructions',
- // TRANS: Form instructions for group create form.
- _('Use this form to create a new group.'));
- }
-
- protected function handlePost()
- {
- parent::handlePost();
-
if (Event::handle('StartGroupSaveForm', array($this))) {
$nickname = Nickname::normalize($this->trimmed('newnickname'), true);
@@ -133,7 +117,6 @@ class NewgroupAction extends FormAction
'Too many aliases! Maximum %d allowed.',
common_config('group', 'maxaliases')),
common_config('group', 'maxaliases')));
- return;
}
if ($private) {
diff --git a/actions/newnotice.php b/actions/newnotice.php
index 692ba765c8..ccc3f94fd4 100644
--- a/actions/newnotice.php
+++ b/actions/newnotice.php
@@ -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);
}
diff --git a/actions/profilesettings.php b/actions/profilesettings.php
index 59a7f6d860..7d3143d4b1 100644
--- a/actions/profilesettings.php
+++ b/actions/profilesettings.php
@@ -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);
diff --git a/actions/register.php b/actions/register.php
index c97201b666..7ab04534f6 100644
--- a/actions/register.php
+++ b/actions/register.php
@@ -27,9 +27,7 @@
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
/**
* An action for registering a new user account
@@ -229,40 +227,38 @@ class RegisterAction extends Action
} else if ($password != $confirm) {
// TRANS: Form validation error displayed when trying to register with non-matching passwords.
$this->showForm(_('Passwords do not match.'));
- } else if ($user = User::register(array('nickname' => $nickname,
+ } else {
+ try {
+ $user = User::register(array('nickname' => $nickname,
'password' => $password,
'email' => $email,
'fullname' => $fullname,
'homepage' => $homepage,
'bio' => $bio,
'location' => $location,
- 'code' => $code))) {
- if (!($user instanceof User)) {
+ 'code' => $code));
+ // success!
+ if (!common_set_user($user)) {
+ // TRANS: Server error displayed when saving fails during user registration.
+ $this->serverError(_('Error setting user.'));
+ }
+ // this is a real login
+ common_real_login(true);
+ if ($this->boolean('rememberme')) {
+ common_debug('Adding rememberme cookie for ' . $nickname);
+ common_rememberme($user);
+ }
+
+ // Re-init language env in case it changed (not yet, but soon)
+ common_init_language();
+
+ Event::handle('EndRegistrationTry', array($this));
+
+ $this->showSuccess();
+ } catch (Exception $e) {
// TRANS: Form validation error displayed when trying to register with an invalid username or password.
- $this->showForm(_('Invalid username or password.'));
- return;
+ $this->showForm($e->getMessage());
}
- // success!
- if (!common_set_user($user)) {
- // TRANS: Server error displayed when saving fails during user registration.
- $this->serverError(_('Error setting user.'));
- }
- // this is a real login
- common_real_login(true);
- if ($this->boolean('rememberme')) {
- common_debug('Adding rememberme cookie for ' . $nickname);
- common_rememberme($user);
- }
-
- // Re-init language env in case it changed (not yet, but soon)
- common_init_language();
-
- Event::handle('EndRegistrationTry', array($this));
-
- $this->showSuccess();
- } else {
- // TRANS: Form validation error displayed when trying to register with an invalid username or password.
- $this->showForm(_('Invalid username or password.'));
}
}
}
diff --git a/actions/replies.php b/actions/replies.php
index 2b5455e23c..49098d0950 100644
--- a/actions/replies.php
+++ b/actions/replies.php
@@ -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;
}
diff --git a/actions/shownotice.php b/actions/shownotice.php
index 2576bcc111..4a1adfd7de 100644
--- a/actions/shownotice.php
+++ b/actions/shownotice.php
@@ -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();
diff --git a/actions/showstream.php b/actions/showstream.php
index 7c38eca14a..8dd6c7deff 100644
--- a/actions/showstream.php
+++ b/actions/showstream.php
@@ -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');
- }
- }
-}
diff --git a/actions/subscribe.php b/actions/subscribe.php
index 4002c9fbb0..320409afa0 100644
--- a/actions/subscribe.php
+++ b/actions/subscribe.php
@@ -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')) {
diff --git a/actions/tagprofile.php b/actions/tagprofile.php
index 871626ef19..79a401c6a9 100644
--- a/actions/tagprofile.php
+++ b/actions/tagprofile.php
@@ -19,7 +19,6 @@
if (!defined('GNUSOCIAL')) { exit(1); }
-require_once INSTALLDIR . '/lib/settingsaction.php';
require_once INSTALLDIR . '/lib/peopletags.php';
class TagprofileAction extends FormAction
@@ -29,36 +28,32 @@ class TagprofileAction extends FormAction
protected $target = null;
protected $form = 'TagProfile';
- protected function prepare(array $args=array())
+ protected function doPreparation()
{
- parent::prepare($args);
-
$id = $this->trimmed('id');
- if (!$id) {
- $this->target = null;
- } else {
+ $uri = $this->trimmed('uri');
+ if (!empty($id)) {
$this->target = Profile::getKV('id', $id);
if (!$this->target instanceof Profile) {
// TRANS: Client error displayed when referring to non-existing profile ID.
$this->clientError(_('No profile with that ID.'));
}
+ } elseif (!empty($uri)) {
+ $this->target = Profile::fromUri($uri);
+ } else {
+ // TRANS: Client error displayed when trying to tag a user but no ID or profile is provided.
+ $this->clientError(_('No profile identifier provided.'));
}
- if ($this->target instanceof Profile && !$this->scoped->canTag($this->target)) {
+ if (!$this->scoped->canTag($this->target)) {
// TRANS: Client error displayed when trying to tag a user that cannot be tagged.
$this->clientError(_('You cannot tag this user.'));
}
- return true;
- }
+ $this->formOpts = $this->target;
- protected function handle()
- {
- if (Event::handle('StartTagProfileAction', array($this, $this->target))) {
- parent::handle();
- Event::handle('EndTagProfileAction', array($this, $this->target));
- }
+ return true;
}
function title()
@@ -115,17 +110,8 @@ class TagprofileAction extends FormAction
}
}
- protected function getForm()
+ protected function doPost()
{
- $class = $this->form.'Form';
- $form = new $class($this, $this->target);
- return $form;
- }
-
- protected function handlePost()
- {
- parent::handlePost(); // Does nothing for now
-
$tagstring = $this->trimmed('tags');
$token = $this->trimmed('token');
@@ -144,22 +130,16 @@ class TagprofileAction extends FormAction
if (!common_valid_profile_tag($tag)) {
// TRANS: Form validation error displayed if a given tag is invalid.
// TRANS: %s is the invalid tag.
- $this->showForm(sprintf(_('Invalid tag: "%s".'), $tag));
- return;
+ throw new ClientException(sprintf(_('Invalid tag: "%s".'), $tag));
}
$tag_priv[$tag] = $private;
}
}
- try {
- $result = Profile_tag::setTags($this->scoped->id, $this->target->id, $tags, $tag_priv);
- if (!$result) {
- throw new Exception('The tags could not be saved.');
- }
- } catch (Exception $e) {
- $this->showForm($e->getMessage());
- return false;
+ $result = Profile_tag::setTags($this->scoped->getID(), $this->target->getID(), $tags, $tag_priv);
+ if (!$result) {
+ throw new ServerException('The tags could not be saved.');
}
if ($this->boolean('ajax')) {
@@ -188,17 +168,4 @@ class TagprofileAction extends FormAction
Event::handle('EndSavePeopletags', array($this, $tagstring));
}
}
-
- function showPageNotice()
- {
- if ($this->error) {
- $this->element('p', 'error', $this->error);
- } else {
- $this->elementStart('div', 'instructions');
- $this->element('p', null,
- // TRANS: Page notice.
- _('Use this form to add your subscribers or subscriptions to lists.'));
- $this->elementEnd('div');
- }
- }
}
diff --git a/classes/Attention.php b/classes/Attention.php
index ef0dcb1df7..c15a118e12 100644
--- a/classes/Attention.php
+++ b/classes/Attention.php
@@ -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'),
),
diff --git a/classes/Avatar.php b/classes/Avatar.php
index 55abc81b33..1722b85b6b 100644
--- a/classes/Avatar.php
+++ b/classes/Avatar.php
@@ -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();
diff --git a/classes/Config.php b/classes/Config.php
index 899396d710..2e8492849c 100644
--- a/classes/Config.php
+++ b/classes/Config.php
@@ -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'),
);
diff --git a/classes/Confirm_address.php b/classes/Confirm_address.php
index 0ed7796ad4..91a84feb77 100644
--- a/classes/Confirm_address.php
+++ b/classes/Confirm_address.php
@@ -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'),
diff --git a/classes/Consumer.php b/classes/Consumer.php
index 68e973bfb0..4121938ed8 100644
--- a/classes/Consumer.php
+++ b/classes/Consumer.php
@@ -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'),
diff --git a/classes/Conversation.php b/classes/Conversation.php
index 56f61c63ab..343668cc49 100644
--- a/classes/Conversation.php
+++ b/classes/Conversation.php
@@ -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'),
),
diff --git a/classes/Deleted_notice.php b/classes/Deleted_notice.php
index 4c17be185a..a9167f19a4 100644
--- a/classes/Deleted_notice.php
+++ b/classes/Deleted_notice.php
@@ -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'),
),
diff --git a/classes/File.php b/classes/File.php
index ce2f940161..242da159c2 100644
--- a/classes/File.php
+++ b/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,51 +133,56 @@ class File extends Managed_DataObject
throw new ServerException('No canonical URL from given URL to process');
}
- $file = File::getKV('url', $given_url);
- if (!$file instanceof File) {
+ $file = null;
+
+ try {
+ $file = File::getByUrl($given_url);
+ } catch (NoResultException $e) {
// First check if we have a lookup trace for this URL already
- $file_redir = File_redirection::getKV('url', $given_url);
- if ($file_redir instanceof File_redirection) {
+ try {
+ $file_redir = File_redirection::getByUrl($given_url);
$file = File::getKV('id', $file_redir->file_id);
if (!$file instanceof File) {
// File did not exist, let's clean up the File_redirection entry
$file_redir->delete();
}
+ } catch (NoResultException $e) {
+ // We just wanted to doublecheck whether a File_thumbnail we might've had
+ // actually referenced an existing File object.
+ }
+ }
+
+ // If we still don't have a File object, let's create one now!
+ if (!$file instanceof File) {
+ // @fixme for new URLs this also looks up non-redirect data
+ // such as target content type, size, etc, which we need
+ // for File::saveNew(); so we call it even if not following
+ // new redirects.
+ $redir_data = File_redirection::where($given_url);
+ if (is_array($redir_data)) {
+ $redir_url = $redir_data['url'];
+ } elseif (is_string($redir_data)) {
+ $redir_url = $redir_data;
+ $redir_data = array();
+ } else {
+ // TRANS: Server exception thrown when a URL cannot be processed.
+ throw new ServerException(sprintf(_("Cannot process URL '%s'"), $given_url));
}
- // If we still don't have a File object, let's create one now!
- if (!$file instanceof File) {
- // @fixme for new URLs this also looks up non-redirect data
- // such as target content type, size, etc, which we need
- // for File::saveNew(); so we call it even if not following
- // new redirects.
- $redir_data = File_redirection::where($given_url);
- if (is_array($redir_data)) {
- $redir_url = $redir_data['url'];
- } elseif (is_string($redir_data)) {
- $redir_url = $redir_data;
- $redir_data = array();
- } else {
- // TRANS: Server exception thrown when a URL cannot be processed.
- throw new ServerException(sprintf(_("Cannot process URL '%s'"), $given_url));
- }
-
- // TODO: max field length
- if ($redir_url === $given_url || strlen($redir_url) > 255 || !$followRedirects) {
- // Save the File object based on our lookup trace
- $file = File::saveNew($redir_data, $given_url);
- } else {
- // This seems kind of messed up... for now skipping this part
- // if we're already under a redirect, so we don't go into
- // horrible infinite loops if we've been given an unstable
- // redirect (where the final destination of the first request
- // doesn't match what we get when we ask for it again).
- //
- // Seen in the wild with clojure.org, which redirects through
- // wikispaces for auth and appends session data in the URL params.
- $file = self::processNew($redir_url, $notice_id, /*followRedirects*/false);
- File_redirection::saveNew($redir_data, $file->id, $given_url);
- }
+ if ($redir_url === $given_url || !$followRedirects) {
+ // Save the File object based on our lookup trace
+ $file = File::saveNew($redir_data, $given_url);
+ } else {
+ // This seems kind of messed up... for now skipping this part
+ // if we're already under a redirect, so we don't go into
+ // horrible infinite loops if we've been given an unstable
+ // redirect (where the final destination of the first request
+ // doesn't match what we get when we ask for it again).
+ //
+ // Seen in the wild with clojure.org, which redirects through
+ // wikispaces for auth and appends session data in the URL params.
+ $file = self::processNew($redir_url, $notice_id, /*followRedirects*/false);
+ File_redirection::saveNew($redir_data, $file->id, $given_url);
}
if (!$file instanceof File) {
@@ -237,12 +253,7 @@ class File extends Managed_DataObject
static function filename(Profile $profile, $origname, $mimetype)
{
- try {
- $ext = common_supported_mime_to_ext($mimetype);
- } catch (Exception $e) {
- // We don't support this mimetype, but let's guess the extension
- $ext = substr(strrchr($mimetype, '/'), 1);
- }
+ $ext = self::guessMimeExtension($mimetype);
// Normalize and make the original filename more URL friendly.
$origname = basename($origname, ".$ext");
@@ -263,6 +274,17 @@ class File extends Managed_DataObject
return $filename;
}
+ static function guessMimeExtension($mimetype)
+ {
+ try {
+ $ext = common_supported_mime_to_ext($mimetype);
+ } catch (Exception $e) {
+ // We don't support this mimetype, but let's guess the extension
+ $ext = substr(strrchr($mimetype, '/'), 1);
+ }
+ return strtolower($ext);
+ }
+
/**
* Validation for as-saved base filenames
*/
@@ -303,7 +325,7 @@ class File extends Managed_DataObject
}
- if (StatusNet::useHTTPS()) {
+ if (GNUsocial::useHTTPS()) {
$sslserver = common_config('attachments', 'sslserver');
@@ -381,6 +403,10 @@ class File extends Managed_DataObject
* @param $crop bool Crop to the max-values' aspect ratio
*
* @return File_thumbnail
+ *
+ * @throws UseFileAsThumbnailException if the file is considered an image itself and should be itself as thumbnail
+ * @throws UnsupportedMediaException if, despite trying, we can't understand how to make a thumbnail for this format
+ * @throws ServerException on various other errors
*/
public function getThumbnail($width=null, $height=null, $crop=false, $force_still=true)
{
@@ -394,67 +420,16 @@ class File extends Managed_DataObject
}
}
- if ($width === null) {
- $width = common_config('thumbnail', 'width');
- $height = common_config('thumbnail', 'height');
- $crop = common_config('thumbnail', 'crop');
- }
-
- if ($height === null) {
- $height = $width;
- $crop = true;
- }
-
- // Get proper aspect ratio width and height before lookup
- // We have to do it through an ImageFile object because of orientation etc.
- // Only other solution would've been to rotate + rewrite uploaded files.
- list($width, $height, $x, $y, $w, $h) =
- $image->scaleToFit($width, $height, $crop);
-
- $params = array('file_id'=> $this->id,
- 'width' => $width,
- 'height' => $height);
- $thumb = File_thumbnail::pkeyGet($params);
- if ($thumb instanceof File_thumbnail) {
- return $thumb;
- }
-
- // throws exception on failure to generate thumbnail
- $outname = "thumb-{$width}x{$height}-" . $image->filename;
- $outpath = self::path($outname);
-
- // The boundary box for our resizing
- $box = array('width'=>$width, 'height'=>$height,
- 'x'=>$x, 'y'=>$y,
- 'w'=>$w, 'h'=>$h);
-
- // Doublecheck that parameters are sane and integers.
- if ($box['width'] < 1 || $box['width'] > common_config('thumbnail', 'maxsize')
- || $box['height'] < 1 || $box['height'] > common_config('thumbnail', 'maxsize')
- || $box['w'] < 1 || $box['x'] >= $image->width
- || $box['h'] < 1 || $box['y'] >= $image->height) {
- // Fail on bad width parameter. If this occurs, it's due to algorithm in ImageFile->scaleToFit
- common_debug("Boundary box parameters for resize of {$image->filepath} : ".var_export($box,true));
- throw new ServerException('Bad thumbnail size parameters.');
- }
-
- common_debug(sprintf('Generating a thumbnail of File id==%u of size %ux%u', $this->id, $width, $height));
- // Perform resize and store into file
- $image->resizeTo($outpath, $box);
-
- // Avoid deleting the original
- if ($image->getPath() != self::path($image->filename)) {
- $image->unlink();
- }
- return File_thumbnail::saveThumbnail($this->id,
- self::url($outname),
- $width, $height,
- $outname);
+ return $image->getFileThumbnail($width, $height, $crop);
}
public function getPath()
{
- return self::path($this->filename);
+ $filepath = self::path($this->filename);
+ if (!file_exists($filepath)) {
+ throw new FileNotFoundException($filepath);
+ }
+ return $filepath;
}
public function getUrl()
@@ -462,7 +437,7 @@ class File extends Managed_DataObject
if (!empty($this->filename)) {
// A locally stored file, so let's generate a URL for our instance.
$url = self::url($this->filename);
- if ($url != $this->url) {
+ if (self::hashurl($url) !== $this->urlhash) {
// For indexing purposes, in case we do a lookup on the 'url' field.
// also we're fixing possible changes from http to https, or paths
$this->updateUrl($url);
@@ -474,16 +449,40 @@ class File extends Managed_DataObject
return $this->url;
}
+ static public function getByUrl($url)
+ {
+ $file = new File();
+ $file->urlhash = self::hashurl($url);
+ if (!$file->find(true)) {
+ throw new NoResultException($file);
+ }
+ return $file;
+ }
+
+ /**
+ * @param string $hashstr String of (preferrably lower case) hexadecimal characters, same as result of 'hash_file(...)'
+ */
+ static public function getByHash($hashstr, $alg=File::FILEHASH_ALG)
+ {
+ $file = new File();
+ $file->filehash = strtolower($hashstr);
+ if (!$file->find(true)) {
+ throw new NoResultException($file);
+ }
+ return $file;
+ }
+
public function updateUrl($url)
{
- $file = File::getKV('url', $url);
+ $file = File::getKV('urlhash', self::hashurl($url));
if ($file instanceof File) {
throw new ServerException('URL already exists in DB');
}
- $sql = 'UPDATE %1$s SET url=%2$s WHERE url=%3$s;';
+ $sql = 'UPDATE %1$s SET urlhash=%2$s, url=%3$s WHERE urlhash=%4$s;';
$result = $this->query(sprintf($sql, $this->__table,
+ $this->_quote((string)self::hashurl($url)),
$this->_quote((string)$url),
- $this->_quote((string)$this->url)));
+ $this->_quote((string)$this->urlhash)));
if ($result === false) {
common_log_db_error($this, 'UPDATE', __FILE__);
throw new ServerException("Could not UPDATE {$this->__table}.url");
@@ -502,9 +501,9 @@ class File extends Managed_DataObject
function blowCache($last=false)
{
- self::blow('file:notice-ids:%s', $this->url);
+ self::blow('file:notice-ids:%s', $this->urlhash);
if ($last) {
- self::blow('file:notice-ids:%s;last', $this->url);
+ self::blow('file:notice-ids:%s;last', $this->urlhash);
}
self::blow('file:notice-count:%d', $this->id);
}
@@ -582,4 +581,54 @@ class File extends Managed_DataObject
return $title ?: null;
}
+
+ static public function hashurl($url)
+ {
+ if (empty($url)) {
+ throw new Exception('No URL provided to hash algorithm.');
+ }
+ return hash(self::URLHASH_ALG, $url);
+ }
+
+ static public function beforeSchemaUpdate()
+ {
+ $table = strtolower(get_called_class());
+ $schema = Schema::get();
+ $schemadef = $schema->getTableDef($table);
+
+ // 2015-02-19 We have to upgrade our table definitions to have the urlhash field populated
+ if (isset($schemadef['fields']['urlhash']) && isset($schemadef['unique keys']['file_urlhash_key'])) {
+ // We already have the urlhash field, so no need to migrate it.
+ return;
+ }
+ echo "\nFound old $table table, upgrading it to contain 'urlhash' field...";
+ // We have to create a urlhash that is _not_ the primary key,
+ // transfer data and THEN run checkSchema
+ $schemadef['fields']['urlhash'] = array (
+ 'type' => 'varchar',
+ 'length' => 64,
+ 'not null' => true,
+ 'description' => 'sha256 of destination URL (url field)',
+ );
+ $schemadef['fields']['url'] = array (
+ 'type' => 'text',
+ 'description' => 'destination URL after following possible redirections',
+ );
+ unset($schemadef['unique keys']);
+ $schema->ensureTable($table, $schemadef);
+ echo "DONE.\n";
+
+ $classname = ucfirst($table);
+ $tablefix = new $classname;
+ // urlhash is hash('sha256', $url) in the File table
+ echo "Updating urlhash fields in $table table...";
+ // Maybe very MySQL specific :(
+ $tablefix->query(sprintf('UPDATE %1$s SET %2$s=%3$s;',
+ $schema->quoteIdentifier($table),
+ 'urlhash',
+ // The line below is "result of sha256 on column `url`"
+ 'SHA2(url, 256)'));
+ echo "DONE.\n";
+ echo "Resuming core schema upgrade...";
+ }
}
diff --git a/classes/File_redirection.php b/classes/File_redirection.php
index 0bcccc6cff..8c64c58a80 100644
--- a/classes/File_redirection.php
+++ b/classes/File_redirection.php
@@ -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...";
+ }
}
diff --git a/classes/File_thumbnail.php b/classes/File_thumbnail.php
index 6a92b07d0c..1250f70404 100644
--- a/classes/File_thumbnail.php
+++ b/classes/File_thumbnail.php
@@ -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))) {
diff --git a/classes/Foreign_link.php b/classes/Foreign_link.php
index a964d87fdd..6176ec43bc 100644
--- a/classes/Foreign_link.php
+++ b/classes/Foreign_link.php
@@ -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'),
diff --git a/classes/Foreign_service.php b/classes/Foreign_service.php
index df9fd5825d..78c1c0cee5 100644
--- a/classes/Foreign_service.php
+++ b/classes/Foreign_service.php
@@ -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'),
),
diff --git a/classes/Foreign_user.php b/classes/Foreign_user.php
index eeaf817876..c1739d318a 100644
--- a/classes/Foreign_user.php
+++ b/classes/Foreign_user.php
@@ -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'),
),
diff --git a/classes/Group_member.php b/classes/Group_member.php
index 14f417758d..392440222b 100644
--- a/classes/Group_member.php
+++ b/classes/Group_member.php
@@ -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'),
),
diff --git a/classes/Invitation.php b/classes/Invitation.php
index a40bd0f330..ca03bb7aa1 100644
--- a/classes/Invitation.php
+++ b/classes/Invitation.php
@@ -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'),
diff --git a/classes/Location_namespace.php b/classes/Location_namespace.php
index 3690ecc044..a841473953 100644
--- a/classes/Location_namespace.php
+++ b/classes/Location_namespace.php
@@ -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'),
),
diff --git a/classes/Managed_DataObject.php b/classes/Managed_DataObject.php
index a628b8bee3..b324984b7f 100644
--- a/classes/Managed_DataObject.php
+++ b/classes/Managed_DataObject.php
@@ -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
+ }
}
diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php
index 2bd9581cf6..3f1945205a 100644
--- a/classes/Memcached_DataObject.php
+++ b/classes/Memcached_DataObject.php
@@ -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);
}
}
}
diff --git a/classes/Nonce.php b/classes/Nonce.php
index 9d8dfece04..d37aade4a8 100644
--- a/classes/Nonce.php
+++ b/classes/Nonce.php
@@ -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'),
diff --git a/classes/Notice.php b/classes/Notice.php
index a69efc45ae..541a72fb09 100644
--- a/classes/Notice.php
+++ b/classes/Notice.php
@@ -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'),
),
@@ -128,7 +128,7 @@ class Notice extends Managed_DataObject
return $def;
}
-
+
/* Notice types */
const LOCAL_PUBLIC = 1;
const REMOTE = 0;
@@ -142,7 +142,7 @@ class Notice extends Managed_DataObject
const FOLLOWER_SCOPE = 8;
protected $_profile = array();
-
+
/**
* Will always return a profile, if anything fails it will
* (through _setProfile) throw a NoProfileException.
@@ -157,7 +157,7 @@ class Notice extends Managed_DataObject
}
return $this->_profile[$this->profile_id];
}
-
+
public function _setProfile(Profile $profile=null)
{
if (!$profile instanceof Profile) {
@@ -268,7 +268,7 @@ class Notice extends Managed_DataObject
}
return $title;
}
-
+
public function getContent()
{
return $this->content;
@@ -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.
*/
@@ -674,7 +684,7 @@ class Notice extends Managed_DataObject
$notice->insert(); // throws exception on failure
// If it's not part of a conversation, it's
// the beginning of a new conversation.
- if (empty($notice->conversation)) {
+ if (empty($notice->conversation)) {
$orig = clone($notice);
// $act->context->conversation will be null if it was not provided
$conv = Conversation::create($notice, $options['conversation']);
@@ -777,7 +787,7 @@ class Notice extends Managed_DataObject
'distribute' => true);
// options will have default values when nothing has been supplied
- $options = array_merge($defaults, $options);
+ $options = array_merge($defaults, $options);
foreach (array_keys($defaults) as $key) {
// Only convert the keynames we specify ourselves from 'defaults' array into variables
$$key = $options[$key];
@@ -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)) {
@@ -957,7 +967,7 @@ class Notice extends Managed_DataObject
// Prepare inbox delivery, may be queued to background.
$stored->distribute();
}
-
+
return $stored;
}
@@ -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
@@ -1187,24 +1193,20 @@ class Notice extends Managed_DataObject
}
protected $_attachments = array();
-
+
function attachments() {
if (isset($this->_attachments[$this->id])) {
return $this->_attachments[$this->id];
}
-
- $f2ps = File_to_post::listGet('post_id', array($this->id));
-
- $ids = array();
-
- foreach ($f2ps[$this->id] as $f2p) {
- $ids[] = $f2p->file_id;
- }
-
- $files = File::multiGet('id', $ids);
+ $f2ps = File_to_post::listGet('post_id', array($this->id));
+ $ids = array();
+ foreach ($f2ps[$this->id] as $f2p) {
+ $ids[] = $f2p->file_id;
+ }
+
+ $files = File::multiGet('id', $ids);
$this->_attachments[$this->id] = $files->fetchAll();
-
return $this->_attachments[$this->id];
}
@@ -1286,7 +1288,7 @@ class Notice extends Managed_DataObject
$root->free();
return $root;
}
-
+
if (is_null($profile)) {
$keypart = sprintf('notice:conversation_root:%d:null', $this->id);
} else {
@@ -1294,7 +1296,7 @@ class Notice extends Managed_DataObject
$this->id,
$profile->id);
}
-
+
$root = self::cacheGet($keypart);
if ($root !== false && $root->inScope($profile)) {
@@ -1707,9 +1709,9 @@ class Notice extends Managed_DataObject
function getReplyProfiles()
{
$ids = $this->getReplies();
-
+
$profiles = Profile::multiGet('id', $ids);
-
+
return $profiles->fetchAll();
}
@@ -1747,9 +1749,9 @@ class Notice extends Managed_DataObject
*
* @return array of Group objects
*/
-
+
protected $_groups = array();
-
+
function getGroups()
{
// Don't save groups for repeats
@@ -1757,27 +1759,24 @@ class Notice extends Managed_DataObject
if (!empty($this->repeat_of)) {
return array();
}
-
+
if (isset($this->_groups[$this->id])) {
return $this->_groups[$this->id];
}
-
+
$gis = Group_inbox::listGet('notice_id', array($this->id));
$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];
}
-
+
function _setGroups($groups)
{
$this->_groups[$this->id] = $groups;
@@ -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();
}
@@ -2159,7 +2148,7 @@ class Notice extends Managed_DataObject
// Unfortunately this is likely to lose tags or URLs
// at the end of long notices.
$content = mb_substr($content, 0, $maxlen - 4) . ' ...';
- }
+ }
// Scope is same as this one's
@@ -2687,89 +2676,69 @@ class Notice extends Managed_DataObject
$scope = self::defaultScope();
}
- // If there's no scope, anyone (even anon) is in scope.
-
- if ($scope == 0) { // Not private
-
- return !$this->isHiddenSpam($profile);
-
- } else { // Private, somehow
-
- // If there's scope, anon cannot be in scope
-
- if (empty($profile)) {
- return false;
- }
-
- // Author is always in scope
-
- if ($this->profile_id == $profile->id) {
- return true;
- }
-
- // Only for users on this site
-
- if (($scope & Notice::SITE_SCOPE) && !$profile->isLocal()) {
- return false;
- }
-
- // Only for users mentioned in the notice
-
- if ($scope & Notice::ADDRESSEE_SCOPE) {
-
- $reply = Reply::pkeyGet(array('notice_id' => $this->id,
- 'profile_id' => $profile->id));
-
- if (!$reply instanceof Reply) {
- return false;
- }
- }
-
- // Only for members of the given group
-
- if ($scope & Notice::GROUP_SCOPE) {
-
- // XXX: just query for the single membership
-
- $groups = $this->getGroups();
-
- $foundOne = false;
-
- foreach ($groups as $group) {
- if ($profile->isMember($group)) {
- $foundOne = true;
- break;
- }
- }
-
- if (!$foundOne) {
- return false;
- }
- }
-
- // Only for followers of the author
-
- $author = null;
-
- if ($scope & Notice::FOLLOWER_SCOPE) {
-
- try {
- $author = $this->getProfile();
- } catch (Exception $e) {
- return false;
- }
-
- if (!Subscription::exists($profile, $author)) {
- return false;
- }
- }
-
+ if ($scope == 0 && !$this->getProfile()->isPrivateStream()) { // Not scoping, so it is public.
return !$this->isHiddenSpam($profile);
}
+
+ // If there's scope, anon cannot be in scope
+ if (empty($profile)) {
+ return false;
+ }
+
+ // Author is always in scope
+ if ($this->profile_id == $profile->id) {
+ return true;
+ }
+
+ // Only for users on this site
+ if (($scope & Notice::SITE_SCOPE) && !$profile->isLocal()) {
+ return false;
+ }
+
+ // Only for users mentioned in the notice
+ if ($scope & Notice::ADDRESSEE_SCOPE) {
+
+ $reply = Reply::pkeyGet(array('notice_id' => $this->id,
+ 'profile_id' => $profile->id));
+
+ if (!$reply instanceof Reply) {
+ return false;
+ }
+ }
+
+ // Only for members of the given group
+ if ($scope & Notice::GROUP_SCOPE) {
+
+ // XXX: just query for the single membership
+
+ $groups = $this->getGroups();
+
+ $foundOne = false;
+
+ foreach ($groups as $group) {
+ if ($profile->isMember($group)) {
+ $foundOne = true;
+ break;
+ }
+ }
+
+ if (!$foundOne) {
+ return false;
+ }
+ }
+
+ if ($scope & Notice::FOLLOWER_SCOPE || $this->getProfile()->isPrivateStream()) {
+
+ if (!Subscription::exists($profile, $this->getProfile())) {
+ return false;
+ }
+ }
+
+ return !$this->isHiddenSpam($profile);
}
function isHiddenSpam($profile) {
-
+
// Hide posts by silenced users from everyone but moderators.
if (common_config('notice', 'hidespam')) {
@@ -2819,7 +2788,7 @@ class Notice extends Managed_DataObject
$skip = array('_profile', '_groups', '_attachments', '_faves', '_replies', '_repeats');
return array_diff($vars, $skip);
}
-
+
static function defaultScope()
{
$scope = common_config('notice', 'defaultscope');
@@ -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)) {
@@ -2847,42 +2815,35 @@ class Notice extends Managed_DataObject
unset($notices[$entry]);
}
}
-
+
return array_values($map);
}
-
+
static function getProfiles(&$notices)
{
$ids = array();
foreach ($notices as $notice) {
$ids[] = $notice->profile_id;
}
-
$ids = array_unique($ids);
-
- return Profile::pivotGet('id', $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;
}
}
-
+
$gids = array_unique($gids);
-
$group = User_group::pivotGet('id', $gids);
-
foreach ($notices as $notice)
{
$grps = array();
@@ -2906,21 +2867,16 @@ 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;
+ $fileIds[] = $f2p->file_id;
}
}
$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);
- }
- }
}
diff --git a/classes/Notice_source.php b/classes/Notice_source.php
index 6f3d2e5634..f31d4411ff 100644
--- a/classes/Notice_source.php
+++ b/classes/Notice_source.php
@@ -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'),
diff --git a/classes/Oauth_application.php b/classes/Oauth_application.php
index b29fdfe41b..d5d38d0d66 100644
--- a/classes/Oauth_application.php
+++ b/classes/Oauth_application.php
@@ -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'),
diff --git a/classes/Oauth_application_user.php b/classes/Oauth_application_user.php
index 007e4d1db6..60b2e8fa2c 100644
--- a/classes/Oauth_application_user.php
+++ b/classes/Oauth_application_user.php
@@ -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'),
),
diff --git a/classes/Oauth_token_association.php b/classes/Oauth_token_association.php
index ec5eae3064..83bc0d8054 100644
--- a/classes/Oauth_token_association.php
+++ b/classes/Oauth_token_association.php
@@ -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'),
),
diff --git a/classes/Profile.php b/classes/Profile.php
index 0b608fbb24..db8326f0f6 100644
--- a/classes/Profile.php
+++ b/classes/Profile.php
@@ -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);
}
diff --git a/classes/Profile_list.php b/classes/Profile_list.php
index 0ad8106e31..2b3d2aa5ad 100644
--- a/classes/Profile_list.php
+++ b/classes/Profile_list.php
@@ -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'),
),
diff --git a/classes/Profile_prefs.php b/classes/Profile_prefs.php
index ee0fa0e27c..27034390f8 100644
--- a/classes/Profile_prefs.php
+++ b/classes/Profile_prefs.php
@@ -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'),
diff --git a/classes/Sms_carrier.php b/classes/Sms_carrier.php
index 7d3a5fc0ca..d37cf42d97 100644
--- a/classes/Sms_carrier.php
+++ b/classes/Sms_carrier.php
@@ -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'),
),
diff --git a/classes/Status_network.php b/classes/Status_network.php
index ffbf9d5a3c..3498b4bd24 100644
--- a/classes/Status_network.php
+++ b/classes/Status_network.php
@@ -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
diff --git a/classes/Subscription.php b/classes/Subscription.php
index 4d7eb524dc..6601bcd6fc 100644
--- a/classes/Subscription.php
+++ b/classes/Subscription.php
@@ -32,9 +32,9 @@ class Subscription extends Managed_DataObject
public $subscribed; // int(4) primary_key not_null
public $jabber; // tinyint(1) default_1
public $sms; // tinyint(1) default_1
- public $token; // varchar(255)
- public $secret; // varchar(255)
- public $uri; // varchar(255)
+ public $token; // varchar(191) not 255 because utf8mb4 takes more space
+ public $secret; // varchar(191) not 255 because utf8mb4 takes more space
+ public $uri; // varchar(191) not 255 because utf8mb4 takes more space
public $created; // datetime() not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
@@ -46,9 +46,9 @@ class Subscription extends Managed_DataObject
'subscribed' => array('type' => 'int', 'not null' => true, 'description' => 'profile being listened to'),
'jabber' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'deliver jabber messages'),
'sms' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'deliver sms messages'),
- 'token' => array('type' => 'varchar', 'length' => 255, 'description' => 'authorization token'),
- 'secret' => array('type' => 'varchar', 'length' => 255, 'description' => 'token secret'),
- 'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universally unique identifier'),
+ 'token' => array('type' => 'varchar', 'length' => 191, 'description' => 'authorization token'),
+ 'secret' => array('type' => 'varchar', 'length' => 191, 'description' => 'token secret'),
+ 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
),
@@ -94,8 +94,12 @@ class Subscription extends Managed_DataObject
if (Event::handle('StartSubscribe', array($subscriber, $other))) {
$otherUser = User::getKV('id', $other->id);
if ($otherUser instanceof User && $otherUser->subscribe_policy == User::SUBSCRIBE_POLICY_MODERATE && !$force) {
- $sub = Subscription_queue::saveNew($subscriber, $other);
- $sub->notify();
+ try {
+ $sub = Subscription_queue::saveNew($subscriber, $other);
+ $sub->notify();
+ } catch (AlreadyFulfilledException $e) {
+ $sub = Subscription_queue::getSubQueue($subscriber, $other);
+ }
} else {
$sub = self::saveNew($subscriber->id, $other->id);
$sub->notify();
@@ -124,7 +128,7 @@ class Subscription extends Managed_DataObject
}
}
- if ($sub instanceof Subscription) { // i.e. not SubscriptionQueue
+ if ($sub instanceof Subscription) { // i.e. not Subscription_queue
Event::handle('EndSubscribe', array($subscriber, $other));
}
}
@@ -132,6 +136,16 @@ class Subscription extends Managed_DataObject
return $sub;
}
+ static function ensureStart(Profile $subscriber, Profile $other, $force=false)
+ {
+ try {
+ $sub = self::start($subscriber, $other, $force);
+ } catch (AlreadyFulfilledException $e) {
+ return self::getSubscription($subscriber, $other);
+ }
+ return $sub;
+ }
+
/**
* Low-level subscription save.
* Outside callers should use Subscription::start()
@@ -232,9 +246,25 @@ class Subscription extends Managed_DataObject
static function exists(Profile $subscriber, Profile $other)
{
- $sub = Subscription::pkeyGet(array('subscriber' => $subscriber->id,
- 'subscribed' => $other->id));
- return ($sub instanceof Subscription);
+ try {
+ $sub = self::getSubscription($subscriber, $other);
+ } catch (NoResultException $e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ static function getSubscription(Profile $subscriber, Profile $other)
+ {
+ // This is essentially a pkeyGet but we have an object to return in NoResultException
+ $sub = new Subscription();
+ $sub->subscriber = $subscriber->id;
+ $sub->subscribed = $other->id;
+ if (!$sub->find(true)) {
+ throw new NoResultException($sub);
+ }
+ return $sub;
}
function asActivity()
diff --git a/classes/Subscription_queue.php b/classes/Subscription_queue.php
index 405eca93fd..878fcf5796 100644
--- a/classes/Subscription_queue.php
+++ b/classes/Subscription_queue.php
@@ -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.
*
diff --git a/classes/Token.php b/classes/Token.php
index 8d8a4d1c11..89b3f4ecf1 100644
--- a/classes/Token.php
+++ b/classes/Token.php
@@ -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'),
diff --git a/classes/User.php b/classes/User.php
index 7a19ae3a0a..f543a75528 100644
--- a/classes/User.php
+++ b/classes/User.php
@@ -34,9 +34,9 @@ class User extends Managed_DataObject
public $__table = 'user'; // table name
public $id; // int(4) primary_key not_null
public $nickname; // varchar(64) unique_key
- public $password; // varchar(255)
- public $email; // varchar(255) unique_key
- public $incomingemail; // varchar(255) unique_key
+ public $password; // varchar(191) not 255 because utf8mb4 takes more space
+ public $email; // varchar(191) unique_key not 255 because utf8mb4 takes more space
+ public $incomingemail; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $emailnotifysub; // tinyint(1) default_1
public $emailnotifyfav; // tinyint(1) default_1
public $emailnotifynudge; // tinyint(1) default_1
@@ -50,8 +50,8 @@ class User extends Managed_DataObject
public $carrier; // int(4)
public $smsnotify; // tinyint(1)
public $smsreplies; // tinyint(1)
- public $smsemail; // varchar(255)
- public $uri; // varchar(255) unique_key
+ public $smsemail; // varchar(191) not 255 because utf8mb4 takes more space
+ public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $autosubscribe; // tinyint(1)
public $subscribe_policy; // tinyint(1)
public $urlshorteningservice; // varchar(50) default_ur1.ca
@@ -69,9 +69,9 @@ class User extends Managed_DataObject
'fields' => array(
'id' => array('type' => 'int', 'not null' => true, 'description' => 'foreign key to profile table'),
'nickname' => array('type' => 'varchar', 'length' => 64, 'description' => 'nickname or username, duped in profile'),
- 'password' => array('type' => 'varchar', 'length' => 255, 'description' => 'salted password, can be null for OpenID users'),
- 'email' => array('type' => 'varchar', 'length' => 255, 'description' => 'email address for password recovery etc.'),
- 'incomingemail' => array('type' => 'varchar', 'length' => 255, 'description' => 'email address for post-by-email'),
+ 'password' => array('type' => 'varchar', 'length' => 191, 'description' => 'salted password, can be null for OpenID users'),
+ 'email' => array('type' => 'varchar', 'length' => 191, 'description' => 'email address for password recovery etc.'),
+ 'incomingemail' => array('type' => 'varchar', 'length' => 191, 'description' => 'email address for post-by-email'),
'emailnotifysub' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of subscriptions'),
'emailnotifyfav' => array('type' => 'int', 'size' => 'tiny', 'default' => null, 'description' => 'Notify by email of favorites'),
'emailnotifynudge' => array('type' => 'int', 'size' => 'tiny', 'default' => 1, 'description' => 'Notify by email of nudges'),
@@ -85,8 +85,8 @@ class User extends Managed_DataObject
'carrier' => array('type' => 'int', 'description' => 'foreign key to sms_carrier'),
'smsnotify' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'whether to send notices to SMS'),
'smsreplies' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'whether to send notices to SMS on replies'),
- 'smsemail' => array('type' => 'varchar', 'length' => 255, 'description' => 'built from sms and carrier'),
- 'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'universally unique identifier, usually a tag URI'),
+ 'smsemail' => array('type' => 'varchar', 'length' => 191, 'description' => 'built from sms and carrier'),
+ 'uri' => array('type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'),
'autosubscribe' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'automatically subscribe to users who subscribe to us'),
'subscribe_policy' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => '0 = anybody can subscribe; 1 = require approval'),
'urlshorteningservice' => array('type' => 'varchar', 'length' => 50, 'default' => 'internal', 'description' => 'service to use for auto-shortening URLs'),
@@ -191,7 +191,8 @@ class User extends Managed_DataObject
* string 'password' (may be missing for eg OpenID registrations)
* string 'code' invite code
* ?string 'uri' permalink to notice; defaults to local notice URL
- * @return mixed User object or false on failure
+ * @return User object
+ * @throws Exception on failure
*/
static function register(array $fields) {
@@ -205,12 +206,8 @@ class User extends Managed_DataObject
$email = common_canonical_email($email);
}
- try {
- $profile->nickname = Nickname::normalize($nickname, true);
- } catch (NicknameException $e) {
- common_log(LOG_WARNING, sprintf('Bad nickname during User registration for %s: %s', $nickname, $e->getMessage()), __FILE__);
- return false;
- }
+ // Normalize _and_ check whether it is in use. Throw NicknameException on failure.
+ $profile->nickname = Nickname::normalize($nickname, true);
$profile->profileurl = common_profile_url($profile->nickname);
@@ -277,7 +274,9 @@ class User extends Managed_DataObject
$id = $profile->insert();
if ($id === false) {
common_log_db_error($profile, 'INSERT', __FILE__);
- return false;
+ $profile->query('ROLLBACK');
+ // TRANS: Profile data could not be inserted for some reason.
+ throw new ServerException(_m('Could not insert profile data for new user.'));
}
$user->id = $id;
@@ -297,7 +296,8 @@ class User extends Managed_DataObject
if ($result === false) {
common_log_db_error($user, 'INSERT', __FILE__);
$profile->query('ROLLBACK');
- return false;
+ // TRANS: User data could not be inserted for some reason.
+ throw new ServerException(_m('Could not insert user data for new user.'));
}
// Everyone is subscribed to themself
@@ -312,7 +312,8 @@ class User extends Managed_DataObject
if (!$result) {
common_log_db_error($subscription, 'INSERT', __FILE__);
$profile->query('ROLLBACK');
- return false;
+ // TRANS: Subscription data could not be inserted for some reason.
+ throw new ServerException(_m('Could not insert subscription data for new user.'));
}
// Mark that this invite was converted
@@ -334,7 +335,8 @@ class User extends Managed_DataObject
if (!$result) {
common_log_db_error($confirm, 'INSERT', __FILE__);
$profile->query('ROLLBACK');
- return false;
+ // TRANS: Email confirmation data could not be inserted for some reason.
+ throw new ServerException(_m('Could not insert email confirmation data for new user.'));
}
}
@@ -352,7 +354,7 @@ class User extends Managed_DataObject
common_log(LOG_WARNING, sprintf("Default user %s does not exist.", $defnick),
__FILE__);
} else {
- Subscription::start($profile, $defuser->getProfile());
+ Subscription::ensureStart($profile, $defuser->getProfile());
}
}
@@ -385,6 +387,10 @@ class User extends Managed_DataObject
Event::handle('EndUserRegister', array($profile));
}
+ if (!$user instanceof User) {
+ throw new ServerException('User could not be registered. Probably an event hook that failed.');
+ }
+
return $user;
}
@@ -687,11 +693,9 @@ class User extends Managed_DataObject
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
-
- function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null)
+ public function repeatedToMe($offset=0, $limit=20, $since_id=null, $max_id=null)
{
- // TRANS: Exception thrown when trying view "repeated to me".
- throw new Exception(_('Not implemented since inbox change.'));
+ return $this->getProfile()->repeatedToMe($offset, $limit, $since_id, $max_id);
}
public static function siteOwner()
@@ -994,6 +998,11 @@ class User extends Managed_DataObject
return $act;
}
+ public function isPrivateStream()
+ {
+ return $this->getProfile()->isPrivateStream();
+ }
+
public function delPref($namespace, $topic)
{
return $this->getProfile()->delPref($namespace, $topic);
diff --git a/classes/User_group.php b/classes/User_group.php
index 875f5d650e..df54b7987c 100644
--- a/classes/User_group.php
+++ b/classes/User_group.php
@@ -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);
}
diff --git a/classes/User_im_prefs.php b/classes/User_im_prefs.php
index cba91ea252..16fd030bb4 100644
--- a/classes/User_im_prefs.php
+++ b/classes/User_im_prefs.php
@@ -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'),
diff --git a/classes/User_username.php b/classes/User_username.php
index b252ee3156..0173a6efc4 100644
--- a/classes/User_username.php
+++ b/classes/User_username.php
@@ -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'),
diff --git a/db/site.sql b/db/site.sql
index c630a83d52..5e9693f5f7 100644
--- a/db/site.sql
+++ b/db/site.sql
@@ -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;
diff --git a/htaccess.sample b/htaccess.sample
index 90e18e72e6..f7513cc0c7 100644
--- a/htaccess.sample
+++ b/htaccess.sample
@@ -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
+
+
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 @@
# 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
diff --git a/index.php b/index.php
index 674099e712..44599d68e9 100644
--- a/index.php
+++ b/index.php
@@ -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));
}
diff --git a/install.php b/install.php
index d72cf69b07..2752c96767 100644
--- a/install.php
+++ b/install.php
@@ -265,10 +265,10 @@ class WebInstaller extends Installer