$bestname = $profile->getBestName();
$sitename = common_config('site', 'name');
$personal = $this->trimmed('personal');
+ $language = $this->trimmed('language');
$addresses = explode("\n", $this->trimmed('addresses'));
foreach ($addresses as $email) {
$this->already[] = $other;
} else {
try {
- Subscription::start($profile, $other);
+ Subscription::ensureStart($profile, $other);
$this->subbed[] = $other;
} catch (Exception $e) {
// subscription failed, but keep working
} catch (NoSuchUserException $e) {
// If email was not known, let's send an invite!
$this->sent[] = $email;
- $this->sendInvitation($email, $user, $personal);
+ $this->sendInvitation($email, $user, $personal, $language);
}
}
}
}
- function sendInvitation($email, $user, $personal)
+ function sendInvitation($email, $user, $personal, $language)
{
$profile = $user->getProfile();
$bestname = $profile->getBestName();
$title = (empty($personal)) ? 'invite' : 'invitepersonal';
// @todo FIXME: i18n issue.
- $inviteTemplate = DocFile::forTitle($title, DocFile::mailPaths());
+ $inviteTemplate = DocFile::forTitle($title, DocFile::mailPaths(), $language);
$body = $inviteTemplate->toHTML(array('inviter' => $bestname,
'inviterurl' => $profile->profileurl,
* @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
* @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(array $args=array())
+ 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);
}
- <<<<<<< HEAD
--
-- return true;
-- }
--
-- /**
-- * Handle a request
-- *
-- * Just show the page. All args already handled.
-- *
-- * @param array $args $_REQUEST data
-- *
-- * @return void
-- */
-- function handle(array $args=array())
-- {
-- parent::handle($args);
-- $this->showPage();
- =======
- >>>>>>> 1442ca16b410d327d7ec6269944144dfa075ff17
}
/**
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);
}
}
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.
$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));
$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');
$this->elementEnd('div');
}
+<<<<<<< HEAD
function isReadOnly(array $args=array())
+=======
+ public function isReadOnly($args)
+>>>>>>> 1442ca16b410d327d7ec6269944144dfa075ff17
{
return true;
}
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)
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 */
'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)'),
'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'),
),
return $def;
}
-
+
/* Notice types */
const LOCAL_PUBLIC = 1;
const REMOTE = 0;
const FOLLOWER_SCOPE = 8;
protected $_profile = array();
-
+
/**
* Will always return a profile, if anything fails it will
* (through _setProfile) throw a NoProfileException.
}
return $this->_profile[$this->profile_id];
}
-
+
public function _setProfile(Profile $profile=null)
{
if (!$profile instanceof Profile) {
}
return $title;
}
-
+
public function getContent()
{
return $this->content;
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.
*/
throw new ClientException(_('You cannot repeat your own notice.'));
}
- if ($repeat->scope != Notice::SITE_SCOPE &&
- $repeat->scope != Notice::PUBLIC_SCOPE) {
+ if ($repeat->isPrivateScope()) {
// TRANS: Client error displayed when trying to repeat a non-public notice.
throw new ClientException(_('Cannot repeat a private notice.'), 403);
}
$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']);
'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];
// Prepare inbox delivery, may be queued to background.
$stored->distribute();
}
-
+
return $stored;
}
}
$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
}
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;
+ $ids[] = $f2p->file_id;
}
-
- $files = File::multiGet('id', $ids);
+ $files = File::multiGet('id', $ids);
$this->_attachments[$this->id] = $files->fetchAll();
-
return $this->_attachments[$this->id];
}
$root->free();
return $root;
}
-
+
if (is_null($profile)) {
$keypart = sprintf('notice:conversation_root:%d:null', $this->id);
} else {
$this->id,
$profile->id);
}
-
+
$root = self::cacheGet($keypart);
if ($root !== false && $root->inScope($profile)) {
function getReplyProfiles()
{
$ids = $this->getReplies();
-
+
$profiles = Profile::multiGet('id', $ids);
-
+
return $profiles->fetchAll();
}
*
* @return array of Group objects
*/
-
+
protected $_groups = array();
-
+
function getGroups()
{
// Don't save groups for repeats
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->_setGroups($groups->fetchAll());
return $this->_groups[$this->id];
// 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
*/
public function getTags()
{
+ // Check default scope (non-private notices)
+ $inScope = (!$this->isPrivateScope());
+
+ // Get current user
+ $user = common_current_user();
+
+ // Is the general scope check okay and the user in logged in?
+ /* NOISY-DEBUG: */ common_debug('[' . __METHOD__ . ':' . __LINE__ . ']: inScope=' . intval($inScope) . ',user[]=' . gettype($user));
+ if (($inScope === TRUE) && ($user instanceof User)) {
+ // Get profile from it
+ $profile = $user->getProfile();
+ /* NOISY-DEBUG: */ common_debug('[' . __METHOD__ . ':' . __LINE__ . ']: inScope=' . intval($inScope) . ',profile[]=' . gettype($profile));
+
+ /*
+ * Check scope, else a privacy leaks happens this way:
+ *
+ * 1) Bob and Alice follow each other and write private notices
+ * (this->scope=2) to each other.
+ * 2) Bob uses tags in his private notice to alice (which she can
+ * read from him).
+ * 3) Alice adds that notice (with tags) to her favorites
+ * ("faving") it.
+ * 4) The tags from Bob's private notice becomes visible in Alice's
+ * profile.
+ *
+ * This has the simple background that the scope is not being
+ * re-checked. This has to be done here at this point because given
+ * above scenario is a privacy leak as the tags may be *really*
+ * private (nobody else shall see them) such as initmate words or
+ * very political words.
+ */
+ $inScope = $this->inScope($profile);
+ /* NOISY-DEBUG: */ common_debug('[' . __METHOD__ . ':' . __LINE__ . ']: inScope=' . intval($inScope) . ' - After inScope() has been called.');
+ }
+
$tags = array();
$keypart = sprintf('notice:tags:%d', $this->id);
} else {
$tag = new Notice_tag();
$tag->notice_id = $this->id;
- if ($tag->find()) {
+
+ // Check scope for privacy-leak protection (see some lines above why)
+ if (($inScope === TRUE) && ($tag->find())) {
while ($tag->fetch()) {
$tags[] = $tag->tag;
}
$scope = self::defaultScope();
}
- // If there's no scope, anyone (even anon) is in scope.
-
- if ($scope == 0) { // Not private
-
+ if ($scope == 0 && !$this->getProfile()->isPrivateStream()) { // Not scoping, so it is public.
return !$this->isHiddenSpam($profile);
+ }
- } else { // Private, somehow
-
- // If there's scope, anon cannot be in scope
+ // If there's scope, anon cannot be in scope
+ if (empty($profile)) {
+ return false;
+ }
- if (empty($profile)) {
- return false;
- }
+ // Author is always in scope
+ if ($this->profile_id == $profile->id) {
+ return true;
+ }
- // Author is always in scope
+ // Only for users on this site
+ if (($scope & Notice::SITE_SCOPE) && !$profile->isLocal()) {
+ return false;
+ }
- if ($this->profile_id == $profile->id) {
- return true;
- }
+ // Only for users mentioned in the notice
+ if ($scope & Notice::ADDRESSEE_SCOPE) {
- // Only for users on this site
+ $reply = Reply::pkeyGet(array('notice_id' => $this->id,
+ 'profile_id' => $profile->id));
- if (($scope & Notice::SITE_SCOPE) && !$profile->isLocal()) {
+ if (!$reply instanceof Reply) {
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) {
+ // Only for members of the given group
+ if ($scope & Notice::GROUP_SCOPE) {
- // XXX: just query for the single membership
+ // XXX: just query for the single membership
- $groups = $this->getGroups();
-
- $foundOne = false;
+ $groups = $this->getGroups();
- foreach ($groups as $group) {
- if ($profile->isMember($group)) {
- $foundOne = true;
- break;
- }
- }
+ $foundOne = false;
- if (!$foundOne) {
- return false;
+ foreach ($groups as $group) {
+ if ($profile->isMember($group)) {
+ $foundOne = true;
+ break;
}
}
- // Only for followers of the author
-
- $author = null;
+ if (!$foundOne) {
+ return false;
+ }
+ }
- if ($scope & Notice::FOLLOWER_SCOPE) {
+ if ($scope & Notice::FOLLOWER_SCOPE || $this->getProfile()->isPrivateStream()) {
- try {
- $author = $this->getProfile();
- } catch (Exception $e) {
- return false;
- }
-
- if (!Subscription::exists($profile, $author)) {
- return false;
- }
+ if (!Subscription::exists($profile, $this->getProfile())) {
+ return false;
}
-
- return !$this->isHiddenSpam($profile);
}
+
+ return !$this->isHiddenSpam($profile);
}
function isHiddenSpam(Profile $profile=null) {
$skip = array('_profile', '_groups', '_attachments', '_faves', '_replies', '_repeats');
return array_diff($vars, $skip);
}
-
+
static function defaultScope()
{
$scope = common_config('notice', 'defaultscope');
static function fillProfiles(array $notices)
{
$map = self::getProfiles($notices);
-
foreach ($notices as $entry=>$notice) {
try {
if (array_key_exists($notice->profile_id, $map)) {
unset($notices[$entry]);
}
}
-
+
return array_values($map);
}
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(array &$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();
static function fillAttachments(array &$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();
$notice->_setRepeats($repeats);
}
}
+
+ /**
+ * Checks whether this notice is in "private scope" (non-public notice)
+ *
+ * @return $isPrivate Whether this notice is private
+ */
+ public function isPrivateScope ()
+ {
+ return ($this->scope != Notice::SITE_SCOPE &&
+ $this->scope != Notice::PUBLIC_SCOPE);
+ }
}
$this->filename = $filename;
}
- static function forTitle($title, $paths)
+ static function forTitle($title, array $paths, $language=null)
{
- if (!is_array($paths)) {
- $paths = array($paths);
- }
-
$filename = null;
if (Event::handle('StartDocFileForTitle', array($title, &$paths, &$filename))) {
}
if (!empty($lang) || !empty($def)) {
- $filename = self::negotiateLanguage($lang, $def);
+ $filename = self::negotiateLanguage($lang, $def, $language);
break;
}
}
}
}
- function toHTML(array $args=null)
+ function toHTML(array $args=array())
{
- if (is_null($args)) {
- $args = array();
- }
-
if (empty($this->contents)) {
$this->contents = file_get_contents($this->filename);
}
$paths = array(INSTALLDIR.'/local/doc-src/',
INSTALLDIR.'/doc-src/');
- $site = StatusNet::currentSite();
+ $site = GNUsocial::currentSite();
-
+
if (!empty($site)) {
array_unshift($paths, INSTALLDIR.'/local/doc-src/'.$site.'/');
}
$paths = array(INSTALLDIR.'/local/mail-src/',
INSTALLDIR.'/mail-src/');
- $site = StatusNet::currentSite();
+ $site = GNUsocial::currentSite();
-
+
if (!empty($site)) {
array_unshift($paths, INSTALLDIR.'/local/mail-src/'.$site.'/');
}
return $paths;
}
- static function negotiateLanguage($filenames, $defaultFilename=null)
+ private static function negotiateLanguage(array $filenames, $defaultFilename=null, $language = null)
{
- // XXX: do this better
-
+ // Default is current language
$langcode = common_language();
+ // Is a language set?
+ if (!empty($language)) {
+ // And is it valid?
+ if (common_valid_language($language)) {
+ // Use this as language (e.g. from form)
+ $langcode = strval($language);
+ }
+ }
+
foreach ($filenames as $filename) {
if (preg_match('/\.'.$langcode.'$/', $filename)) {
return $filename;
define('GNUSOCIAL_ENGINE', 'GNU social');
define('GNUSOCIAL_ENGINE_URL', 'https://www.gnu.org/software/social/');
-define('GNUSOCIAL_BASE_VERSION', '1.1.3');
-define('GNUSOCIAL_LIFECYCLE', 'release'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
+define('GNUSOCIAL_BASE_VERSION', '1.2.0');
+define('GNUSOCIAL_LIFECYCLE', 'dev'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
define('GNUSOCIAL_VERSION', GNUSOCIAL_BASE_VERSION . '-' . GNUSOCIAL_LIFECYCLE);
- define('GNUSOCIAL_CODENAME', 'Not decided yet');
+ define('GNUSOCIAL_CODENAME', 'Only a fixed bug is a good bug.');
define('AVATAR_PROFILE_SIZE', 96);
define('AVATAR_STREAM_SIZE', 48);
function addPlugin($name, array $attrs=array())
{
- return StatusNet::addPlugin($name, $attrs);
+ return GNUsocial::addPlugin($name, $attrs);
}
function _have_config()
{
- return StatusNet::haveConfig();
+ return GNUsocial::haveConfig();
}
function common_get_temp_dir()
public $__table = 'fave'; // table name
public $notice_id; // int(4) primary_key not_null
public $user_id; // int(4) primary_key not_null
- public $uri; // varchar(255)
+ public $uri; // varchar(191) not 255 because utf8mb4 takes more space not 255 because utf8mb4 takes more space
public $created; // datetime multiple_key not_null
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
'fields' => array(
'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'notice that is the favorite'),
'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user who likes this 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 this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
),
* @throws Exception on failure
*/
static function addNew(Profile $actor, Notice $target) {
+ if (self::existsForProfile($target, $actor)) {
+ // TRANS: Client error displayed when trying to mark a notice as favorite that already is a favorite.
+ throw new AlreadyFulfilledException(_('You have already favorited this!'));
+ }
+
$act = new Activity();
$act->type = ActivityObject::ACTIVITY;
$act->verb = ActivityVerb::FAVORITE;
return $stored;
}
+ public function removeEntry(Profile $actor, Notice $target)
+ {
+ $fave = new Fave();
+ $fave->user_id = $actor->getID();
+ $fave->notice_id = $target->getID();
+ if (!$fave->find(true)) {
+ // TRANS: Client error displayed when trying to remove a 'favor' when there is none in the first place.
+ throw new AlreadyFulfilledException(_('This is already not favorited.'));
+ }
+
+ $result = $fave->delete();
+ if ($result === false) {
+ common_log_db_error($fave, 'DELETE', __FILE__);
+ // TRANS: Server error displayed when removing a favorite from the database fails.
+ throw new ServerException(_('Could not delete favorite.'));
+ }
+
+ Fave::blowCacheForProfileId($actor->getID());
+ Fave::blowCacheForNoticeId($target->getID());
+ }
+
// exception throwing takeover!
public function insert()
{
*
* @return array Array of Fave objects
*/
- static public function byNotice($notice)
+ static public function byNotice(Notice $notice)
{
if (!isset(self::$_faves[$notice->id])) {
self::fillFaves(array($notice->id));