--- /dev/null
+[submodule "statistics_plugin"]
+ path = statistics_plugin
+ url = https://dev.pztrn.name/statistics_plugin.git
return null;
}
- static function saveNew($user, $address, $addressType, $extra=null)
+ static function saveNew(User $user, $address, $addressType, $extra=null)
{
$ca = new Confirm_address();
}
}
- if ($width === null) {
+ if ($width === null || $width < 1) {
$width = common_config('thumbnail', 'width');
$height = common_config('thumbnail', 'height');
$crop = common_config('thumbnail', 'crop');
}
- if ($height === null) {
+ if ($height === null || $height < 1) {
$height = $width;
$crop = true;
}
+ // Debug log (convert crop to int to have TRUE being displayed as 1 and FALSE as 0)
+ common_debug('[' . __METHOD__ . ':' . __LINE__ . ']: width=' . $width . ',height=' . $height . ',crop=' . intval($crop));
+
// 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.
|| $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("Boundary box parameters for resize of {$this->filepath} : ".var_export($box,true));
+ throw new ServerException('Bad thumbnail size parameters. maxsize=' .
+ common_config('thumbnail', 'maxsize') .
+ ',box[width]=' . $box['width'] .
+ ',box[height]=' . $box['height'] .
+ ',box[w]=' . $box['w'] .
+ ',box[h]=' . $box['h'] .
+ ',box[x]=' . $box['x'] .
+ ',box[y]=' . $box['y'] .
+ ',this->width=' . $this->width .
+ ',this->heigh=' . $this->height .
+ ',this->filepath=' . $this->filepath .
+ ',this->filename=' . $this->filename
+ );
}
common_debug(sprintf('Generating a thumbnail of File id==%u of size %ux%u', $this->id, $width, $height));
return $out_url;
}
- function saveNew($data, $file_id, $url) {
+ function saveNew(array $data, $file_id, $url) {
$file_redir = new File_redirection;
$file_redir->url = $url;
$file_redir->file_id = $file_id;
* @param int $file_id
*/
public static function saveNew($data, $file_id) {
+ // @TODO Must be an object (see below code)
+ assert(is_object($data));
+
if (!empty($data->thumbnail_url)) {
// Non-photo types such as video will usually
// show us a thumbnail, though it's not required.
* @return Notice
* @throws ClientException
*/
- static function saveNew($profile_id, $content, $source, array $options=null) {
+ static function saveNew($profile_id, $content, $source, array $options=array()) {
$defaults = array('uri' => null,
'url' => null,
'conversation' => null, // URI of conversation
'object_type' => null,
'verb' => null);
- if (!empty($options) && is_array($options)) {
+ /*
+ * Above type-hint is already array, so simply count it, this saves
+ * "some" CPU cycles.
+ */
+ if (count($options) > 0) {
$options = array_merge($defaults, $options);
- extract($options);
- } else {
- extract($defaults);
}
+ extract($options);
+
if (!isset($is_local)) {
$is_local = Notice::LOCAL_PUBLIC;
}
array('server' => null,
'dir' => INSTALLDIR . '/file/',
'path' => $_path . '/file/',
+ 'chmod' => 0644, // Access rights (chmod) for any attachments
'sslserver' => null,
'sslpath' => null,
'ssl' => null,
}
}
- function toHTML($args=null)
+ function toHTML(array $args=null)
{
if (is_null($args)) {
$args = array();
if ($this->rotate == 0) {
// No rotational difference, just copy it as-is
@copy($this->filepath, $outpath);
+
+ // And set chmod
+ @chmod($outpath, common_config('attachments', 'chmod'));
return $outpath;
} elseif (abs($this->rotate) == 90) {
// Box is rotated 90 degrees in either direction,
throw new Exception(_('Unknown file type'));
}
+ // Always chmod 0644 (default) to have other processes (e.g. queue daemon read it)
+ @chmod($outpath, common_config('attachments', 'chmod'));
+
imagedestroy($image_src);
imagedestroy($image_dest);
}
* 'location_id' ID of location
* 'location_ns' Namespace of location
*/
- function __construct($action, $options=null)
+ function __construct(Action $action, array $options = array())
{
// XXX: ??? Is this to keep notice forms distinct?
// Do we have to worry about sub-second race conditions?
parent::__construct($action);
- if (is_null($options)) {
- $options = array();
- }
-
$this->actionName = $action->trimmed('action');
$prefill = array('content', 'inreplyto', 'lat',
*
* @access private
*/
-function common_find_mentions($text, $notice)
+function common_find_mentions($text, Notice $notice)
{
try {
$sender = Profile::getKV('id', $notice->profile_id);
common_local_url('AccountManagementControlDocument'));
}
- function onStartShowHTML($action)
+ function onStartShowHTML(Action $action)
{
//Account management discovery link
header('Link: <'.common_local_url('AccountManagementControlDocument').'>; rel="'. AccountManagerPlugin::AM_REL.'"; type="application/json"');
return true;
}
- function onNoticeDeleteRelated($notice) {
+ function onNoticeDeleteRelated(Notice $notice) {
$score = Spam_score::getKV('notice_id', $notice->id);
if (!empty($score)) {
$score->delete();
return true;
}
- function onUserRightsCheck($profile, $right, &$result) {
+ function onUserRightsCheck(Profile $profile, $right, &$result) {
switch ($right) {
case self::REVIEWSPAM:
case self::TRAINSPAM:
public $score; // float
public $created; // datetime
- function saveNew($notice, $result) {
+ function saveNew(Notice $notice, $result) {
$score = new Spam_score();
*
* @return boolean hook value
*/
- function onStartRegisterUser(&$user, &$profile)
+ function onStartRegisterUser(User &$user, Profile &$profile)
{
$homepage = strtolower($profile->homepage);
);
}
- static function saveNew($profile, $title, $content, $options=null)
+ static function saveNew(Profile $profile, $title, $content, array $options = array())
{
- if (is_null($options)) {
- $options = array();
- }
-
$be = new Blog_entry();
$be->id = (string) new UUID();
$be->profile_id = $profile->id;
}
}
- static function fromNotice($notice)
+ static function fromNotice(Notice $notice)
{
return Blog_entry::getKV('uri', $notice->uri);
}
}
}
- function onStartNoticeSave($notice)
+ function onStartNoticeSave(Notice $notice)
{
$args = $this->testArgs($notice);
common_debug("Blogspamnet args = " . print_r($args, TRUE));
*
* @return boolean hook value
*/
- function onUserRightsCheck($profile, $right, &$result)
+ function onUserRightsCheck(Profile $profile, $right, &$result)
{
if ($right == self::IMPORTDELICIOUS) {
$result = !$profile->isSilenced();
*
* @return boolean hook value
*/
- function onEndProfileSettingsActions($action)
+ function onEndProfileSettingsActions(Action $action)
{
$user = common_current_user();
*
* @return Bookmark found bookmark or null
*/
- static function getByNotice($notice)
+ static function getByNotice(Notice $notice)
{
return self::getKV('uri', $notice->uri);
}
*
* @return Bookmark bookmark found or null
*/
- static function getByURL($profile, $url)
+ static function getByURL(Profile $profile, $url)
{
$nb = new Bookmark();
*
* @return Notice saved notice
*/
- static function saveNew($profile, $title, $url, $rawtags, $description,
- $options=null)
+ static function saveNew(Profile $profile, $title, $url, $rawtags, $description,
+ array $options=array())
{
if (!common_valid_http_url($url)) {
throw new ClientException(_m('Only web bookmarks can be posted (HTTP or HTTPS).'));
throw new ClientException(_m('Bookmark already exists.'));
}
- if (empty($options)) {
- $options = array();
- }
-
if (array_key_exists('uri', $options)) {
$other = Bookmark::getKV('uri', $options['uri']);
if (!empty($other)) {
*
* @param object $notice notice is going to be saved
*/
- public function onStartNoticeSave($notice){
+ public function onStartNoticeSave(Notice $notice) {
if(!preg_match('/<span class="rtl">/', $notice->rendered) && self::isRTL($notice->content))
$notice->rendered = '<span class="rtl">'.$notice->rendered.'</span>';
return true;
* @param string $content
* @return boolean
*/
- public static function isRTL($content){
+ public static function isRTL($content) {
$content = self::getClearText($content);
$words = explode(' ', $content);
$rtl = 0;
* @param string $str
* @return boolean
*/
- public static function startsWithRTLCharacter($str){
+ public static function startsWithRTLCharacter($str ){
if (strlen($str) < 1) {
return false;
}
* @param string $str
* @return string
*/
- private static function getClearText($str){
+ private static function getClearText($str) {
$str = preg_replace('/@[^ ]+|![^ ]+|#[^ ]+/u', '', $str); // reply, tag, group
$str = preg_replace('/^RT[: ]{1}| RT | RT: |^RD[: ]{1}| RD | RD: |[♺♻:]/u', '', $str); // redent, retweet
$str = preg_replace("/[ \r\t\n]+/", ' ', trim($str)); // remove spaces
*
* @return boolean hook flag
*/
- function onEndShowStatusNetScripts($action) {
+ public function onEndShowStatusNetScripts(Action $action) {
$name = $action->arg('action');
if ($name == 'invite') {
$action->script($this->getPath() . '/js/whitelistinvite.js');
return true;
}
- function onRequireValidatedEmailPlugin_Override($user, &$knownGood)
+ public function onRequireValidatedEmailPlugin_Override(User $user, &$knownGood)
{
$knownGood = (!empty($user->email) && $this->matchesWhitelist($user->email));
return true;
}
- function onEndValidateUserEmail($user, $email, &$valid)
+ // @TODO Most callers are given NULL as first argument
+ public function onEndValidateUserEmail($user, $email, &$valid)
{
if ($valid) { // it's otherwise valid
if (!$this->matchesWhitelist($email)) {
return true;
}
- function onStartAddEmailAddress($user, $email)
+ public function onStartAddEmailAddress(User $user, $email)
{
if (!$this->matchesWhitelist($email)) {
// TRANS: Exception thrown when an e-mail address does not match the site's domain whitelist.
);
}
- static function saveNew($profile, $start_time, $end_time, $title, $location, $description, $url, $options=array())
+ static function saveNew(Profile $profile, $start_time, $end_time, $title, $location, $description, $url, array $options=array())
{
if (array_key_exists('uri', $options)) {
$other = Happening::getKV('uri', $options['uri']);
return Notice::getKV('uri', $this->uri);
}
- static function fromNotice($notice)
+ static function fromNotice(Notice $notice)
{
return Happening::getKV('uri', $notice->uri);
}
);
}
- function saveNew($profile, $event, $verb, $options=array())
+ function saveNew(Profile $profile, $event, $verb, array $options = array())
{
if (array_key_exists('uri', $options)) {
$other = RSVP::getKV('uri', $options['uri']);
$this->response);
}
- static function toHTML($profile, $event, $response)
+ static function toHTML(Profile $profile, Event $event, $response)
{
$fmt = null;
public $description; // text
public $profile_id; // int
- public static function getByNotice($notice)
+ public static function getByNotice(Notice $notice)
{
return self::getKV('uri', $notice->uri);
}
);
}
- static function saveNew(Profile $profile, $photo_uri, $thumb_uri, $title, $description, $options=array())
+ static function saveNew(Profile $profile, $photo_uri, $thumb_uri, $title, $description, array $options=array())
{
$photo = new Photo();
public $url; // varchar (255)
public $profile_id; // int
- public static function getByNotice($notice)
+ public static function getByNotice(Notice $notice)
{
return self::getKV('uri', $notice->uri);
}
);
}
- static function saveNew(Profile $profile, $url, $options=array())
+ static function saveNew(Profile $profile, $url, array $options=array())
{
$vid = new Video();
return JSMin::minify($code);
}
- static function minifyCss($code, $options = array()) {
+ static function minifyCss($code, array $options = array()) {
require_once('Minify/CSS.php');
return Minify_CSS::minify($code,$options);
}
class WAP20Plugin extends Plugin
{
- function onStartShowHTML($action)
+ function onStartShowHTML(Action $action)
{
}
parent::__construct();
}
- function onStartShowHTML($action)
+ function onStartShowHTML(Action $action)
{
// XXX: This should probably graduate to WAP20Plugin
$action->elementEnd('address');
}
- function onStartShowAside($action)
+ function onStartShowAside(Action $action)
{
if ($this->serveMobile) {
return false;
}
}
- function onStartShowLocalNavBlock($action)
+ function onStartShowLocalNavBlock(Action $action)
{
if ($this->serveMobile) {
// @todo FIXME: "Show Navigation" / "Hide Navigation" needs i18n
return true;
}
- function onUserRightsCheck($profile, $right, &$result)
+ function onUserRightsCheck(Profile $profile, $right, &$result)
{
if (in_array($right, self::$rights)) {
// Hrm.... really we should confirm that the *other* user isn't privleged. :)
return true;
}
- function onEndGrantRole($profile, $role)
+ function onEndGrantRole(Profile $profile, $role)
{
$modlog = new ModLog();
$modlog->profile_id = $profile->id;
$cur = common_current_user();
-
+
if (!empty($cur)) {
$modlog->moderator_id = $cur->id;
}
return true;
}
- function onEndRevokeRole($profile, $role)
+ function onEndRevokeRole(Profile $profile, $role)
{
$modlog = new ModLog();
$modlog->profile_id = $profile->id;
$cur = common_current_user();
-
+
if (!empty($cur)) {
$modlog->moderator_id = $cur->id;
}
}
}
- function onUserRightsCheck($profile, $right, &$result) {
+ function onUserRightsCheck(Profile $profile, $right, &$result) {
switch ($right) {
case self::VIEWMODLOG:
$result = ($profile->hasRole(Profile_role::MODERATOR) || $profile->hasRole('modhelper'));
*
* @param ProfileListItem $item
*/
- function onStartProfileListItemProfile($item)
+ function onStartProfileListItemProfile(ProfileListItem $item)
{
$this->showProfileOptions($item->out, $item->profile->getProfile());
return true;
public $private_key;
public $servers;
- function onStartNoticeSave($notice)
+ function onStartNoticeSave(Notice $notice)
{
if ( $this->public_key ) {
//Check spam
*
* @return boolean hook value
*/
- function onNoticeDeleteRelated($notice)
+ function onNoticeDeleteRelated(Notice $notice)
{
$nt = Notice_title::getKV('notice_id', $notice->id);
*
* @return boolean hook value
*/
- function onStartShowHeadTitle($action)
+ function onStartShowHeadTitle(Action $action)
{
$actionName = $action->trimmed('action');
*
* @return string title of the notice, or null if none
*/
- static function fromNotice($notice)
+ static function fromNotice(Notice $notice)
{
$nt = Notice_title::getKV('notice_id', $notice->id);
if (empty($nt)) {
return true;
}
- function onStartTagProfileAction($action, $profile)
+ function onStartTagProfileAction(Action $action, Profile $profile)
{
$err = null;
$uri = $action->trimmed('uri');
/*
* If the field being looked for is URI look for the profile
*/
- function onStartProfileCompletionSearch($action, $profile, $search_engine) {
+ public function onStartProfileCompletionSearch(Action $action, Profile $profile, $search_engine) {
if ($action->field == 'uri') {
$profile->joinAdd(array('id', 'user:id'));
$profile->whereAdd('uri LIKE "%' . $profile->escape($q) . '%"');
* @param array &$mention in/out param: set of found mentions
* @return boolean hook return value
*/
- function onEndFindMentions(Profile $sender, $text, &$mentions)
+ function onEndFindMentions(Profile $sender, $text, array &$mentions)
{
$matches = array();
* @param Profile &$profile
* @return hook return code
*/
- function onStartCommandGetProfile($command, $arg, &$profile)
+ public function onStartCommandGetProfile(Command $command, $arg, Profile &$profile = null)
{
$oprofile = $this->pullRemoteProfile($arg);
if ($oprofile instanceof Ostatus_profile && !$oprofile->isGroup()) {
* @param User_group &$group
* @return hook return code
*/
- function onStartCommandGetGroup($command, $arg, &$group)
+ function onStartCommandGetGroup(Command $command, $arg, User_group &$group = null)
{
$oprofile = $this->pullRemoteProfile($arg);
if ($oprofile instanceof Ostatus_profile && $oprofile->isGroup()) {
return true;
}
- function onEndShowStatusNetScripts($action) {
+ public function onEndShowStatusNetScripts(Action $action) {
$action->script($this->path('js/ostatus.js'));
return true;
}
* @param int $file_id
*/
public static function saveNew($data, $file_id) {
+ // @TODO Must be an object
+ assert(is_object($data));
+
$file_oembed = new File_oembed;
$file_oembed->file_id = $file_id;
if (!isset($data->version)) {
*
* @return Poll found poll or null
*/
- static function getByNotice($notice)
+ static function getByNotice(Notice $notice)
{
return self::getKV('uri', $notice->uri);
}
*
* @return Notice saved notice
*/
- static function saveNew($profile, $question, $opts, $options=null)
+ static function saveNew(Profile $profile, $question, $opts, array $options = array())
{
- if (empty($options)) {
- $options = array();
- }
-
$p = new Poll();
$p->id = UUID::gen();
*
* @return Poll_response found response or null
*/
- static function getByNotice($notice)
+ static function getByNotice(Notice $notice)
{
return self::getKV('uri', $notice->uri);
}
*
* @return Notice saved notice
*/
- static function saveNew($profile, $poll, $selection, $options=null)
+ static function saveNew(Profile $profile, $poll, $selection, array $options=array())
{
- if (empty($options)) {
- $options = array();
- }
-
if (!$poll->isValidSelection($selection)) {
// TRANS: Client exception thrown when responding to a poll with an invalid option.
throw new ClientException(_m('Invalid poll selection.'));
*
* @return QnA_Answer found response or null
*/
- static function getByNotice($notice)
+ static function getByNotice(Notice $notice)
{
$answer = self::getKV('uri', $notice->uri);
if (empty($answer)) {
return Notice::getKV('uri', $this->uri);
}
- static function fromNotice($notice)
+ static function fromNotice(Notice $notice)
{
return QnA_Answer::getKV('uri', $notice->uri);
}
);
}
- static function toHTML($profile, $question, $answer)
+ static function toHTML(Profile $profile, $question, $answer)
{
$notice = $question->getNotice();
*
* @return Notice saved notice
*/
- static function saveNew($profile, $question, $text, $options = null)
+ static function saveNew(Profile $profile, $question, $text, array $options = array())
{
- if (empty($options)) {
- $options = array();
- }
-
$answer = new QnA_Answer();
$answer->id = UUID::gen();
$answer->profile_id = $profile->id;
*
* @return Question found question or null
*/
- static function getByNotice($notice)
+ static function getByNotice(Notice $notice)
{
return self::getKV('uri', $notice->uri);
}
return $a->count();
}
- static function fromNotice($notice)
+ static function fromNotice(Notice $notice)
{
return QnA_Question::getKV('uri', $notice->uri);
}
return self::toString($this->getProfile(), $this);
}
- static function toHTML($profile, $question)
+ static function toHTML(Profile $profile, $question)
{
$notice = $question->getNotice();
*
* @return Notice saved notice
*/
- static function saveNew($profile, $title, $description, $options = array())
+ static function saveNew(Profile $profile, $title, $description, array $options = array())
{
$q = new QnA_Question();
*
* @return void
*/
- function __construct($out = null, $title = null, $description = null, $options = null)
+ function __construct($out = null, $title = null, $description = null, array $options = array())
{
parent::__construct($out);
$this->title = $title;
return true;
}
+ /**
+ * Called when someone tries to register.
+ *
+ * We check the IP here to determine if it goes over any of our
+ * configured limits.
+ *
+ * @param Action $action Action that is being executed
+ *
+ * @return boolean hook value
+ */
function onStartRegistrationTry(Action $action)
{
$resp = recaptcha_check_answer ($this->private_key,
*
* @return boolean hook value
*/
- public function onEndGrantRole($profile, $role)
+ public function onEndGrantRole(Profile $profile, $role)
{
if (!self::$enabled) {
return true;
*
* @return bool hook result code
*/
- function onStartNoticeSave($notice)
+ function onStartNoticeSave(Notice $notice)
{
$user = User::getKV('id', $notice->profile_id);
if (!empty($user)) { // it's a remote notice
*
* @return bool hook result code
*/
- function onStartRegisterUser(&$user, &$profile)
+ function onStartRegisterUser(User &$user, Profile &$profile)
{
$email = $user->email;
*
* @return bool
*/
- protected function validated($user)
+ protected function validated(User $user)
{
// The email field is only stored after validation...
// Until then you'll find them in confirm_address.
return $ts;
}
+ /**
+ * Getter for Profile instance
+ *
+ * @return $profile Profile instance
+ */
+ public function getProfile () {
+ assert($this->profile_id > 0);
+ $profile = new Profile();
+ $profile->id = $this->profile_id;
+ return $profile;
+ }
+
/**
* End a search subscription!
*
parent::__construct();
}
- function onStartNoticeSave($notice)
+ function onStartNoticeSave(Notice $notice)
{
$notice->rendered = preg_replace_callback('/spotify:[a-z]{5,6}:[a-z0-9]{22}/i',
"renderSpotifyURILink",
--- /dev/null
+../../statistics_plugin/README.md
\ No newline at end of file
--- /dev/null
+../../statistics_plugin/StatisticsPlugin-gnusocial.php
\ No newline at end of file
--- /dev/null
+../../statistics_plugin/actions/
\ No newline at end of file
* Kind of dirty, but if pulling an external data feed into an account
* that may be what you want.
*
- * @param Notice $notice
- * @return mixed Notice on successful repeat, true if already repeated, false on failure
+ * @param Profile $profile
+ * @param Notice $notice
+ * @return mixed Notice on successful repeat, true if already repeated, false on failure
*/
- protected function copyNotice($profile, $notice)
+ protected function copyNotice(Profile $profile, Notice $notice)
{
$options = array('is_local' => Notice::LOCAL_PUBLIC,
'url' => $notice->getUrl(), // pass through the foreign link...
return $tags;
}
+
+ /**
+ * Getter for Profile instance
+ *
+ * @return $profile Profile instance
+ */
+ public function getProfile () {
+ assert($this->profile_id > 0);
+ $profile = new Profile();
+ $profile->id = $this->profile_id;
+ return $profile;
+ }
}
* @param boolean &$result out, result of the check
*
* @return boolean hook result
+ * @TODO Other implementing classes expect Profile here!!!
+ * @WARNING
*/
function onUserRightsCheck($user, $right, &$result)
{
return true;
}
+ /**
+ * Called when someone tries to register.
+ *
+ * We check the IP here to determine if it goes over any of our
+ * configured limits.
+ *
+ * @param Action $action Action that is being executed
+ *
+ * @return boolean hook value
+ */
function onStartRegistrationTry(Action $action)
{
$this->_checkMaxUsers();
/**
* Add a link header for LRDD Discovery
*/
- public function onStartShowHTML($action)
+ public function onStartShowHTML(Action $action)
{
if ($action instanceof ShowstreamAction) {
$acct = 'acct:'. $action->profile->nickname .'@'. common_config('site', 'server');
--- /dev/null
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// Abort if called from a web server
+if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
+ print "This script must be run from the command line\n";
+ exit();
+}
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+require_once(INSTALLDIR . '/scripts/commandline.inc.php');
+error_reporting(E_ALL | E_STRICT);
+
+$relative = common_path('group/');
+print('Searching for profiles with relative=' . $relative . ' ...' . PHP_EOL);
+
+$profile = new Profile();
+$profile->whereAdd("`profileurl` LIKE '" . $relative . "%'");
+$cnt = $profile->find();
+
+print('Found ' . $cnt . ' profiles.' . PHP_EOL);
+
+if ($cnt == 0) {
+ // Nothing to do
+ print('All profiles are fixed. :-)' . PHP_EOL);
+ exit;
+}
+
+$profiles = array();
+
+while ($profile->fetch()) {
+ array_push($profiles, "'" . $profile->nickname . "'");
+}
+
+// Cleanup
+unset($profile);
+
+print('Have ' . count($profiles) . ' profiles found.' . PHP_EOL);
+
+// Make sure both are equal
+assert(count($profiles) == $cnt);
+
+print('Looking for matching user groups without local groups ...' . PHP_EOL);
+
+$group = new User_group();
+$group->whereAdd("`nickname` IN (" . implode(', ', $profiles) . ") AND `uri` NOT LIKE '" . $relative . "%'");
+$cnt = $group->find();
+
+print('Found ' . $cnt . ' groups.' . PHP_EOL);
+
+while ($group->fetch()) {
+ print('Fixing profile for group #' . $group->id . ',nickname=' . $group->nickname . ' to ' . $group->uri . ' ...' . PHP_EOL);
+
+ if ($group->profile_id < 1) {
+ print('INCONSISTENCY - Group ' . $group->nickname . ' has no profile_id set. Cannot fix!' . PHP_EOL);
+ }
+
+ $profile = new Profile();
+ $profile->id = $group->profile_id;
+ $cnt2 = $profile->find();
+
+ if ($cnt2 == 0) {
+ print('INCONSISTENCY - Cannot find profile ' . $group->nickname . ' - skipped!' . PHP_EOL);
+ continue;
+ } elseif ($cnt2 > 1) {
+ print('INCONSISTENCY - Group ' . $group->nickname . ' has more than one (' . $cnt2 . ') profiles - skipped!' . PHP_EOL);
+ continue;
+ } else {
+ // Dummy fetch
+ $profile->fetch();
+ }
+
+ $original = clone($profile);
+ $profile->profileurl = $group->uri;
+
+ $result = $profile->update($original);
+ if (!$result) {
+ common_log_db_error($profile, 'UPDATE', __FILE__);
+ }
+}
--- /dev/null
+Subproject commit 441a38c20487faf6078e70f1055f20a5412a2fa1