diff options
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/backupuser.php | 44 | ||||
-rw-r--r-- | scripts/commandline.inc | 67 | ||||
-rw-r--r-- | scripts/importtwitteratom.php | 26 | ||||
-rwxr-xr-x | scripts/install_cli.php | 2 | ||||
-rw-r--r-- | scripts/restoreuser.php | 376 | ||||
-rwxr-xr-x | scripts/update_po_templates.php | 42 | ||||
-rwxr-xr-x | scripts/update_translations.php | 2 |
7 files changed, 490 insertions, 69 deletions
diff --git a/scripts/backupuser.php b/scripts/backupuser.php new file mode 100644 index 000000000..49fc1cefd --- /dev/null +++ b/scripts/backupuser.php @@ -0,0 +1,44 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2010 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/>. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); + +$shortoptions = 'i:n:f:'; +$longoptions = array('id=', 'nickname=', 'file='); + +$helptext = <<<END_OF_EXPORTACTIVITYSTREAM_HELP +exportactivitystream.php [options] +Export a StatusNet user history to a file + + -i --id ID of user to export + -n --nickname nickname of the user to export + -f --file file to export to (default STDOUT) + +END_OF_EXPORTACTIVITYSTREAM_HELP; + +require_once INSTALLDIR.'/scripts/commandline.inc'; + +try { + $user = getUser(); + $actstr = new UserActivityStream($user); + print $actstr->getString(); +} catch (Exception $e) { + print $e->getMessage()."\n"; + exit(1); +} diff --git a/scripts/commandline.inc b/scripts/commandline.inc index a475e11d0..9390890ef 100644 --- a/scripts/commandline.inc +++ b/scripts/commandline.inc @@ -177,3 +177,70 @@ function get_option_value($opt, $alt=null) return null; } + +class NoUserArgumentException extends Exception +{ +} + +function getUser() +{ + $user = null; + + if (have_option('i', 'id')) { + $id = get_option_value('i', 'id'); + $user = User::staticGet('id', $id); + if (empty($user)) { + throw new Exception("Can't find user with id '$id'."); + } + } else if (have_option('n', 'nickname')) { + $nickname = get_option_value('n', 'nickname'); + $user = User::staticGet('nickname', $nickname); + if (empty($user)) { + throw new Exception("Can't find user with nickname '$nickname'"); + } + } else { + throw new NoUserArgumentException("No user argument specified."); + } + + return $user; +} + +/** "Printf not quiet" */ + +function printfnq() +{ + if (have_option('q', 'quiet')) { + return null; + } + + $cargs = func_num_args(); + + if ($cargs == 0) { + return 0; + } + + $args = func_get_args(); + $format = array_shift($args); + + return vprintf($format, $args); +} + +/** "Print when verbose" */ + +function printfv() +{ + if (!have_option('v', 'verbose')) { + return null; + } + + $cargs = func_num_args(); + + if ($cargs == 0) { + return 0; + } + + $args = func_get_args(); + $format = array_shift($args); + + return vprintf($format, $args); +}
\ No newline at end of file diff --git a/scripts/importtwitteratom.php b/scripts/importtwitteratom.php index c12e3b91a..a29526f27 100644 --- a/scripts/importtwitteratom.php +++ b/scripts/importtwitteratom.php @@ -36,30 +36,6 @@ END_OF_IMPORTTWITTERATOM_HELP; require_once INSTALLDIR.'/scripts/commandline.inc'; require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php'; -function getUser() -{ - $user = null; - - if (have_option('i', 'id')) { - $id = get_option_value('i', 'id'); - $user = User::staticGet('id', $id); - if (empty($user)) { - throw new Exception("Can't find user with id '$id'."); - } - } else if (have_option('n', 'nickname')) { - $nickname = get_option_value('n', 'nickname'); - $user = User::staticGet('nickname', $nickname); - if (empty($user)) { - throw new Exception("Can't find user with nickname '$nickname'"); - } - } else { - show_help(); - exit(1); - } - - return $user; -} - function getAtomFeedDocument() { $filename = get_option_value('f', 'file'); @@ -113,7 +89,7 @@ function importActivityStream($user, $doc) $html = htmLawed($html, $config); - $content = html_entity_decode(strip_tags($html)); + $content = html_entity_decode(strip_tags($html), ENT_QUOTES, 'UTF-8'); $notice = Notice::saveNew($user->id, $content, diff --git a/scripts/install_cli.php b/scripts/install_cli.php index 61fbe18ef..dadbcf66f 100755 --- a/scripts/install_cli.php +++ b/scripts/install_cli.php @@ -208,7 +208,7 @@ END_HELP; $breakout = preg_replace('/<a[^>+]\bhref="(.*)"[^>]*>(.*)<\/a>/', '\2 <\1>', $html); - return html_entity_decode(strip_tags($breakout)); + return html_entity_decode(strip_tags($breakout), ENT_QUOTES, 'UTF-8'); } } diff --git a/scripts/restoreuser.php b/scripts/restoreuser.php new file mode 100644 index 000000000..82eb9bbaa --- /dev/null +++ b/scripts/restoreuser.php @@ -0,0 +1,376 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2010 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/>. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); + +$shortoptions = 'i:n:f:'; +$longoptions = array('id=', 'nickname=', 'file='); + +$helptext = <<<END_OF_RESTOREUSER_HELP +restoreuser.php [options] +Restore a backed-up user file to the database. If +neither ID or name provided, will create a new user. + + -i --id ID of user to export + -n --nickname nickname of the user to export + -f --file file to read from (STDIN by default) + +END_OF_RESTOREUSER_HELP; + +require_once INSTALLDIR.'/scripts/commandline.inc'; +require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php'; + +function getActivityStreamDocument() +{ + $filename = get_option_value('f', 'file'); + + if (empty($filename)) { + show_help(); + exit(1); + } + + if (!file_exists($filename)) { + throw new Exception("No such file '$filename'."); + } + + if (!is_file($filename)) { + throw new Exception("Not a regular file: '$filename'."); + } + + if (!is_readable($filename)) { + throw new Exception("File '$filename' not readable."); + } + + printfv(_("Getting backup from file '$filename'.")."\n"); + + $xml = file_get_contents($filename); + + $dom = DOMDocument::loadXML($xml); + + if ($dom->documentElement->namespaceURI != Activity::ATOM || + $dom->documentElement->localName != 'feed') { + throw new Exception("'$filename' is not an Atom feed."); + } + + return $dom; +} + +function importActivityStream($user, $doc) +{ + $feed = $doc->documentElement; + + $subjectEl = ActivityUtils::child($feed, Activity::SUBJECT, Activity::SPEC); + + if (!empty($subjectEl)) { + $subject = new ActivityObject($subjectEl); + printfv(_("Backup file for user %s (%s)")."\n", $subject->id, Ostatus_profile::getActivityObjectNickname($subject)); + } else { + throw new Exception("Feed doesn't have an <activity:subject> element."); + } + + if (is_null($user)) { + printfv(_("No user specified; using backup user.")."\n"); + $user = userFromSubject($subject); + } + + $entries = $feed->getElementsByTagNameNS(Activity::ATOM, 'entry'); + + printfv(_("%d entries in backup.")."\n", $entries->length); + + for ($i = $entries->length - 1; $i >= 0; $i--) { + try { + $entry = $entries->item($i); + + $activity = new Activity($entry, $feed); + + switch ($activity->verb) { + case ActivityVerb::FOLLOW: + subscribeProfile($user, $subject, $activity); + break; + case ActivityVerb::JOIN: + joinGroup($user, $activity); + break; + case ActivityVerb::POST: + postNote($user, $activity); + break; + default: + throw new Exception("Unknown verb: {$activity->verb}"); + } + } catch (Exception $e) { + print $e->getMessage()."\n"; + continue; + } + } +} + +function subscribeProfile($user, $subject, $activity) +{ + $profile = $user->getProfile(); + + if ($activity->objects[0]->id == $subject->id) { + + $other = $activity->actor; + $otherUser = User::staticGet('uri', $other->id); + + if (!empty($otherUser)) { + $otherProfile = $otherUser->getProfile(); + } else { + throw new Exception("Can't force remote user to subscribe."); + } + // XXX: don't do this for untrusted input! + Subscription::start($otherProfile, $profile); + + } else if (empty($activity->actor) || $activity->actor->id == $subject->id) { + + $other = $activity->objects[0]; + $otherUser = User::staticGet('uri', $other->id); + + if (!empty($otherUser)) { + $otherProfile = $otherUser->getProfile(); + } else { + $oprofile = Ostatus_profile::ensureActivityObjectProfile($other); + $otherProfile = $oprofile->localProfile(); + } + + Subscription::start($profile, $otherProfile); + } else { + throw new Exception("This activity seems unrelated to our user."); + } +} + +function joinGroup($user, $activity) +{ + // XXX: check that actor == subject + + $uri = $activity->objects[0]->id; + + $group = User_group::staticGet('uri', $uri); + + if (empty($group)) { + $oprofile = Ostatus_profile::ensureActivityObjectProfile($activity->objects[0]); + if (!$oprofile->isGroup()) { + throw new Exception("Remote profile is not a group!"); + } + $group = $oprofile->localGroup(); + } + + assert(!empty($group)); + + if (Event::handle('StartJoinGroup', array($group, $user))) { + Group_member::join($group->id, $user->id); + Event::handle('EndJoinGroup', array($group, $user)); + } +} + +// XXX: largely cadged from Ostatus_profile::processNote() + +function postNote($user, $activity) +{ + $note = $activity->objects[0]; + + $sourceUri = $note->id; + + $notice = Notice::staticGet('uri', $sourceUri); + + if (!empty($notice)) { + // This is weird. + $orig = clone($notice); + $notice->profile_id = $user->id; + $notice->update($orig); + return; + } + + // Use summary as fallback for content + + if (!empty($note->content)) { + $sourceContent = $note->content; + } else if (!empty($note->summary)) { + $sourceContent = $note->summary; + } else if (!empty($note->title)) { + $sourceContent = $note->title; + } else { + // @fixme fetch from $sourceUrl? + // @todo i18n FIXME: use sprintf and add i18n. + throw new ClientException("No content for notice {$sourceUri}."); + } + + // Get (safe!) HTML and text versions of the content + + $rendered = purify($sourceContent); + $content = html_entity_decode(strip_tags($rendered), ENT_QUOTES, 'UTF-8'); + + $shortened = common_shorten_links($content); + + $options = array('is_local' => Notice::LOCAL_PUBLIC, + 'uri' => $sourceUri, + 'rendered' => $rendered, + 'replies' => array(), + 'groups' => array(), + 'tags' => array(), + 'urls' => array()); + + // Check for optional attributes... + + if (!empty($activity->time)) { + $options['created'] = common_sql_date($activity->time); + } + + if ($activity->context) { + // Any individual or group attn: targets? + + list($options['groups'], $options['replies']) = filterAttention($activity->context->attention); + + // Maintain direct reply associations + // @fixme what about conversation ID? + if (!empty($activity->context->replyToID)) { + $orig = Notice::staticGet('uri', + $activity->context->replyToID); + if (!empty($orig)) { + $options['reply_to'] = $orig->id; + } + } + + $location = $activity->context->location; + + if ($location) { + $options['lat'] = $location->lat; + $options['lon'] = $location->lon; + if ($location->location_id) { + $options['location_ns'] = $location->location_ns; + $options['location_id'] = $location->location_id; + } + } + } + + // Atom categories <-> hashtags + + foreach ($activity->categories as $cat) { + if ($cat->term) { + $term = common_canonical_tag($cat->term); + if ($term) { + $options['tags'][] = $term; + } + } + } + + // Atom enclosures -> attachment URLs + foreach ($activity->enclosures as $href) { + // @fixme save these locally or....? + $options['urls'][] = $href; + } + + $saved = Notice::saveNew($user->id, + $content, + 'restore', // TODO: restore the actual source + $options); + + return $saved; +} + +function filterAttention($attn) +{ + $groups = array(); + $replies = array(); + + foreach (array_unique($attn) as $recipient) { + + // Is the recipient a local user? + + $user = User::staticGet('uri', $recipient); + + if ($user) { + // @fixme sender verification, spam etc? + $replies[] = $recipient; + continue; + } + + // Is the recipient a remote group? + $oprofile = Ostatus_profile::ensureProfileURI($recipient); + + if ($oprofile) { + if (!$oprofile->isGroup()) { + // may be canonicalized or something + $replies[] = $oprofile->uri; + } + continue; + } + + // Is the recipient a local group? + // @fixme uri on user_group isn't reliable yet + // $group = User_group::staticGet('uri', $recipient); + $id = OStatusPlugin::localGroupFromUrl($recipient); + + if ($id) { + $group = User_group::staticGet('id', $id); + if ($group) { + // Deliver to all members of this local group if allowed. + $profile = $sender->localProfile(); + if ($profile->isMember($group)) { + $groups[] = $group->id; + } else { + common_log(LOG_INFO, "Skipping reply to local group {$group->nickname} as sender {$profile->id} is not a member"); + } + continue; + } else { + common_log(LOG_INFO, "Skipping reply to bogus group $recipient"); + } + } + } + + return array($groups, $replies); +} + +function userFromSubject($subject) +{ + $user = User::staticGet('uri', $subject->id); + + if (empty($user)) { + $attrs = + array('nickname' => Ostatus_profile::getActivityObjectNickname($subject), + 'uri' => $subject->id); + + $user = User::register($attrs); + } + + $profile = $user->getProfile(); + Ostatus_profile::updateProfile($profile, $subject); + + // FIXME: Update avatar + return $user; +} + +function purify($content) +{ + $config = array('safe' => 1, + 'deny_attribute' => 'id,style,on*'); + return htmLawed($content, $config); +} + +try { + try { + $user = getUser(); + } catch (NoUserArgumentException $noae) { + $user = null; + } + $doc = getActivityStreamDocument(); + importActivityStream($user, $doc); +} catch (Exception $e) { + print $e->getMessage()."\n"; + exit(1); +} diff --git a/scripts/update_po_templates.php b/scripts/update_po_templates.php index f10f20842..a0ab5819d 100755 --- a/scripts/update_po_templates.php +++ b/scripts/update_po_templates.php @@ -81,45 +81,6 @@ END; chdir($old); } -function do_translatewiki_plugin($basedir, $plugin) -{ - $yamldir = "$basedir/locale/TranslateWiki"; - if (!file_exists($yamldir)) { - mkdir($yamldir); - } - $outfile = "$yamldir/StatusNet-{$plugin}.yml"; - $pluginlc = strtolower( $plugin ); - $data = <<<END ---- -BASIC: - id: out-statusnet-{$pluginlc} - label: StatusNet - {$plugin} - namespace: NS_STATUSNET - description: "{{int:bw-desc-statusnet-plugin}}" - class: FileBasedMessageGroup - display: out/statusnet/{$pluginlc} - -FILES: - class: GettextFFS - sourcePattern: %GROUPROOT%/statusnet/plugins/{$plugin}/locale/{$plugin}.pot - targetPattern: statusnet/plugins/{$plugin}/locale/%CODE%/LC_MESSAGES/{$plugin}.po - codeMap: - en-gb: en_GB - no: nb - pt-br: pt_BR - zh-hans: zh_CN - zh-hant: zh_TW - -MANGLER - class: StringMatcher - prefix: {$pluginlc}- - patterns: - - "*" - -END; - file_put_contents($outfile, $data); -} - function get_plugins($dir) { $plugins = array(); @@ -168,7 +129,6 @@ function update_plugin($basedir, $name) $dir = "$basedir/plugins/$name"; if (plugin_using_gettext($dir)) { do_update_plugin($dir, $name); - do_translatewiki_plugin($basedir, $name); return true; } else { return false; @@ -200,8 +160,6 @@ foreach ($args as $arg) { } } - - if ($all || $core) { echo "core..."; update_core(INSTALLDIR, 'statusnet'); diff --git a/scripts/update_translations.php b/scripts/update_translations.php index 89d937e9d..1fe513b13 100755 --- a/scripts/update_translations.php +++ b/scripts/update_translations.php @@ -77,7 +77,7 @@ foreach ($languages as $language) { http_build_query(array( 'title' => 'Special:Translate', 'task' => 'export-to-file', - 'group' => 'out-statusnet', + 'group' => 'out-statusnet-core', 'language' => $twnCode)); $lcdir = INSTALLDIR . '/locale/' . $code; |