Jump to content

Colonel_mortis

Clients
  • Joined

  • Last visited

Everything posted by Colonel_mortis

  1. Reported to me in https://linustechtips.com/topic/1443429-if-theres-a-username-behind-the-cursor-when-youre-reacting-to-a-post-the-hover-card-is-appended-to-the-bottom-page/ (there's a video there which illustrates the issue pretty well). To reproduce: Find a post that somebody else has reacted to Open the react menu, and position the cursor above the username of the member that has already reacted, eg on funny in Click to react to the post Have the request to ?do=showReactionsComment complete: After the reaction menu has closed, so that the hovercard starts getting triggered due to the hover event on the username Before the hovercard has finished loading The hovercard will appear at the bottom of the page rather than in the correct location under the cursor This happens because you take a reference to the hovered element when triggering the hovercard, but then when ?do=showReactionsComment completes the whole reactions block gets replaced, so the referenced element is no longer in the DOM. I'm not sure what the right solution is here, though it could probably be helped by checking that the element exists in the DOM when trying to position the hovercard and aborting if not.
  2. Also had some members reporting today that the most recent posts were still not visible after the deletion had completed. It does look like the cached post count (used for computing the page count) was pretty far off: mysql> SELECT posts FROM forums_topics WHERE tid=901907; +--------+ | posts | +--------+ | 143021 | +--------+ mysql> SELECT COUNT(*) FROM forums_posts WHERE topic_id=901907 AND queued=0; +----------+ | COUNT(*) | +----------+ | 143048 | +----------+ I'm not sure what caused the discrepancy to be this big, but this looks like a pretty classic race condition. Resyncing the comment counts (which would have happened organically if anyone had replied in the past few hours) has fixed it, and it's probably not straight forward to reengineer to avoid this, but it does suck.
  3. I do understand moving around pages, but there are clearly some optimizations that can be done to the elasticsearch utilisation because it should not need to reindex all 150k documents 10k times. That topic's db record also seems to be corrupt, but I need to look into that further (I think the posts field in the db is out of sync with the actual number of replies, but have not verified it).
  4. I have a topic that had >150k posts. A member has requested that their account and posts be deleted, which includes about 10k posts in that topic (and 4k posts in other topics combined). That deletion has been running for the past 20 hours, maxing out the CPU on my server the whole time, and is about 71% complete. The massive topic is having issues where pages are disappearing and reappearing, I think due to consistency issues due to the rate of updates to that topic (although solving that without transactions might be hard), and submitting new posts is very sluggish. The CPU is mostly being consumed by elasticsearch, I think because each post results in every post in the topic being updated in the index. There must be a better way to do that, batching the updates by topic or something.
  5. The symptom of this for regular status updates is that when you submit the status, it gets stuck on "Saving", despite having submitted successfully:
  6. If you have something like <div data-controller="foo"> <div data-controller="bar"></div> </div> ips.controller.register("foo", { initialize: function() { ips.controller.cleanContentsOf(this.scope); } }); The controller on bar will be initialized on the dead element, and never cleaned up: ips.controller.register("bar", { initialize: function() { console.log(document.contains(this.scope[0])); // => false }, destroy: function() { console.log("destroyed"); // never called } }); This happens because ips.controller#_findControllers is called once to get all controllers to load, then the controllers are run one by one. If the foo controller is run first (and I'm not sure if that's guaranteed to be the case), cleanContentsOf will drop the bar element, but it won't run the destructor because the controller hasn't been initialized yet. When all of that logic has finished running, it then runs the bar controller, even though it is no longer applicable. Sure, you might say, but nobody would call cleanContentsOf synchronously during init, right? Yes, you do. Concretely, the problem this causes me is that the commentFeed of a status update exists twice, one in the document and one detatched. This means addToCommentFeed events end up getting processed by both, the detached one first, and the detached one ends up throwing an exception. There are a few different ways this could be fixed: Fix the double initialization of profiles. Seems straight forward enough, and that appears to be the only instance of this issue at the moment. Guard ips.controller#initControllerOnElem with node.contains(elem), to ensure that the node is still a child Run the destructor code from cleanContentsOf in a `setImmediate` block, to give the other controllers an opportunity to be initialized before getting cleaned up
  7. Noticed this locally while testing out an unrelated thing, reproed here. Post a status update from your profile (or the create menu, or whatever) Refresh (open) your profile Status update has disappeared "See my activity" Status update isn't there, and there's no filter sidebar entry for status updates or replies I've not looked into why this is happening, other than that there is no entry in the system log (while not IN_DEV), and rebuilding the search index didn't resolve it (though it was also empty after rebuilding, so YMMV). I no longer use (stock IPS) status updates on my site, and the impression I got from the lack of support previously was that we were the primary user of them, so maybe nobody will notice?
  8. The database changes related to the image scanner (adding core_files_temp.requires_moderation, core_attachments.attach_moderation_status, and core_image_scanner_logs) were added to the upgrade routine for 105013 (4.5.0 beta 1) rather than one of the 4.7 releases. This doesn't cause problems for most people because step 2 of the upgrade routine is to check the database schema, although this could cause problems in future if future upgrade steps act on this new table and someone is upgrading all the way though (and similar bugs in future could be more of a problem if they aren't just straight forward add queries). I ran into this because I replaced the upgrade wizard with a significantly (order of magnitude) faster CLI implementation, but that script had omitted the databaseCheck step.
  9. /var/www/html/ips/applications/downloads/hooks/apiForum.php 20 ERROR Method _delete (public) does not have same visibility as in IPS\Node\Api\_NodeController (protected) (H102) /var/www/html/ips/applications/cms/hooks/apiForum.php 20 ERROR Method _delete (public) does not have same visibility as in IPS\Node\Api\_NodeController (protected) (H102) This isn't causing me any problems, but I added this lint rule because changing protected -> public will break any other hooks that still override as protected (since if they end up overriding from public back to protected, that will cause a fatal error).
  10. Email when you get alerted Ability to view your previous alerts (and respond to them where appropriate) Ability to respond to send the response pm to multiple people (though have to be careful about exposing the content of the alert to other members accidentally) It would also be nice if you could eg automatically (based on a checkbox or something) send an alert to the author when a topic is moved, so they are reminded to post in the right place without clogging the forum up with move links or posting in the topic.
  11. To reproduce, enable security questions in the account security menu. The URL is /settings/account-security/&act=enable&type=questions&_new=1&csrfKey=(omitted)
  12. In member history, when the spam service action was to moderate their posts.
  13. history_spam_defense_5 should translate to " Spam Defense checked and returned score <code>%s</code> - moderator approval required on posts." or something like that.
  14. Hmm yeah I guess that works, would be nicer if the eval logic was changed to have it extend \StdClass or something though.
  15. Having updated my local dev environment to PHP 8.0, I'm now getting PHP Fatal error: Cannot use "parent" when current class scope has no parent in /var/www/html/ips_themes/system/Theme/Theme.php(2633) : eval()'d code on line 29 during the multiredirect, causing it to fall back to the manual redirect (and succeed due to the lock in compileTemplates still being held by the dead request). This is being caused by a theme hook that is manually overriding one of the template methods public function userReputation($member, $types, $currentAppModule, $currentType, $table, $reactions) { // ... my logic return parent::userReputation($member, $types, $currentAppModule, $currentType, $table, $reactions); } which is a supported thing to do. This is causing problems now because as of PHP 8.0 it is a fatal compile time error if the code references `parent` but is in a class that does not have a parent. Please can you update \IPS\Theme::compileTemplates (and \IPS\cms\Theme::compileTemplates) to avoid this, possibly by having the hook class extend \StdClass (or some other placeholder) so the compile time check passes?
  16. To reproduce: Click https://invisioncommunity.com/profile/536475-colonel_mortis/ (note that this will not occur again if you refresh the page or use the browser back/forwards buttons - you need to actually click the link to trigger it) Observe that the page shows up for a moment, then changes to a loading spinner, before finally loading again a few seconds later. All of the page data was reloaded from the server in the process. This can be fairly jarring, especially for users on slower connections. This is because ips.profile.main.js calls History.replaceState from setup. setup is called from initialize after the statechange event handler has already been bound. I'm not sure why you're calling History.replaceState there, but it seems like it should be safe to reorder initialize so you call setup before adding the statechange event handler.
  17. If a topic is mod queued because the member has content moderation enabled, or if a post within an existing topic is mod queued by the profanity filter, the member's post count is not incremented. However, if a new topic gets mod queued by the profanity filter, the member's post count is still incremented. When the topic is approved, their post count is incremented a second time, and any subsequent approvals also result in additional post count increases. To reproduce: Add a word to the word filters, and set it to require moderator approval Check your current post count ($n) Post a new topic containing the banned word, so it becomes mod queued Observe that your post count has increased to $n+1 Approve the topic Observe that your post count has incremented a second time to $n+2 Edit the topic, but leave the banned word in, so it becomes mod queued again on submission (post count is still $n+2) Approve the topic again Observe that your post count has incremented again, to $n+3 Continue editing the post ad nauseum to continue to boost the post count further Aside from the obvious issue that this increases post counts too much, this is also an issue when there are spam prevention rules set up based on having a certain number of approved content items - if members need posts to be approved until they have a post count of 5, their first 5 topics will be mod queued but all subsequent ones will be immediately visible. This is happening because \IPS\Content::checkProfanityFilters doesn't update $this->queued when $this is the first post in a topic, so the subsequent check of $obj->hidden() in \IPS\Content\Comment::create passes and the member's post count is incremented. I believe this bug could also cause wider issues, although I'm not aware of any specifically - the code seems to generally expect that posts with queued=0 are in an unhidden topic, but that is not the case with posts in topics that get mod queued due to the profanity filter (both the first post and, if the first post is edited and gets reenqueued, all other posts in the topic).
  18. I'm a bit late, but I have some updated numbers based on the past 3 months of registrations. In the latest data: Precision (proportion of users with a spam score >1 that are actually spammers) is 81%, which is up significantly on the 51% from 2020 Recall (proportion of spammers that get a spam score >1) is 24%, which is down slightly (but probably within margin of error) on the 28% from 2020 However, the number of spammers relative to actual users has increased since the data in the original post, which means the precision is not comparable. Correcting for that changes the precision to 66%, which is still a decent improvement over 2020. The biggest change for us though is that the spam service can mod queue members rather than blocking their registration outright, which means the sub-optimal precision is not as important as it was. This, combined with a collection of banned words, banned links, and custom spam rules (via a custom plugin), has meant that over 90% of the spam posts were mod queued before being posted.
  19. There are a couple more /src/applications/core/modules/front/system/widgets.php:249:addToStack( 'widget_adv__custom__desc', NULL, array( 'sprintf' => array( \IPS\Request::i()->block ) ) ) /src/applications/blog/sources/Blog/Blog.php:1265:addToStack( static::$titleLangPrefix . $this->_id . static::$descriptionLangSuffix , NULL, array( 'removeLazyLoad' => TRUE ) ) /src/applications/core/modules/admin/promotion/promote.php:467:addToStack( 'promote_facebook_page_prefix', NULL, array( 'sprintf' => array( $data[0] ) ) ) /src/applications/core/modules/admin/promotion/promote.php:473:addToStack( 'promote_facebook_group_prefix', NULL, array( 'sprintf' => array( $data[0] ) ) ) /src/applications/core/modules/admin/promotion/seo.php:159:addToStack( 'use_robotstxt_warning_off', NULL, ['sprintf' => [$rootUrl]] ) /src/applications/core/modules/admin/promotion/seo.php:163:addToStack( 'use_robotstxt_warning_download', NULL, ['sprintf' => [$rootUrl]] ) /src/applications/core/modules/admin/promotion/seo.php:170:addToStack( 'use_robotstxt_warning_existing_off', NULL, ['sprintf' => [ rtrim( \IPS\Http\Url::baseUrl(), '/' ) ] ] ) /src/applications/core/modules/admin/promotion/seo.php:174:addToStack( 'use_robotstxt_warning_existing_download', NULL, ['sprintf' => [ rtrim( \IPS\Http\Url::baseUrl(), '/' ) ] ] ) /src/applications/core/modules/front/system/register.php:1286:addToStack( 'set_password_title', NULL, array( 'sprintf' => array( $member->name ) ) ) /src/applications/core/modules/front/clubs/view.php:1635:addToStack( "add_page_to_club", NULL, array( "sprintf" => array( $this->club->name ) ) ) /src/applications/core/extensions/core/Notifications/Achievements.php:159:addToStack( 'notification__new_badge_content', NULL, [ 'sprintf' => [ $badge->_title ] ] ) /src/applications/core/extensions/core/Notifications/Achievements.php:160:addToStack( 'notification__new_badge', NULL, [ 'sprintf' => [ $badge->_title ] ] ) /src/applications/core/extensions/core/Notifications/Achievements.php:192:addToStack( 'notification__new_badge', NULL, [ 'sprintf' => [ $badge->_title ] ] ) /src/applications/core/modules/admin/overview/files.php:865:addToStack( 'file_config_log', NULL, array( 'sprintf' => array( $method['method'] ) ) ) /src/applications/core/modules/admin/support/support.php:396:addToStack('health_vapid_key_check_fail_exception', NULL, [ 'sprintf' => $ex->getMessage() ] ) /src/applications/core/extensions/core/MemberFilter/Lastvisit.php:248:addToStack( 'member_filter_core_lastvisit_range_desc', NULL, array( 'sprintf' => array( $start->localeDate(), $end->localeDate() ) ) ) /src/applications/core/extensions/core/MemberFilter/Lastvisit.php:253:addToStack( 'member_filter_core_lastvisit_days_desc', NULL, array( 'sprintf' => array( $filters['days'] ) ) ) /src/applications/core/extensions/core/MemberFilter/Lastvisit.php:257:addToStack( 'member_filter_core_lastvisit_days_lt_desc', NULL, array( 'sprintf' => array( $filters['days_lt'] ) ) ) /src/applications/core/extensions/core/MemberFilter/Lastpost.php:263:addToStack( 'member_filter_core_lastpost_range_desc', NULL, array( 'sprintf' => array( $start->localeDate(), $end->localeDate() ) ) ) /src/applications/core/extensions/core/MemberFilter/Lastpost.php:268:addToStack( 'member_filter_core_lastpost_days_desc', NULL, array( 'sprintf' => array( $filters['days'] ) ) ) /src/applications/core/extensions/core/MemberFilter/Lastpost.php:272:addToStack( 'member_filter_core_lastpost_days_lt_desc', NULL, array( 'sprintf' => array( $filters['days_lt'] ) ) ) /src/applications/core/extensions/core/MemberFilter/Achievements.php:336:addToStack( 'member_filter_core_cheev_points_gt_desc', NULL, array( 'sprintf' => array( $filters['achievement_points_value'] ) ) ) /src/applications/core/extensions/core/MemberFilter/Achievements.php:339:addToStack( 'member_filter_core_cheev_points_lt_desc', NULL, array( 'sprintf' => array( $filters['achievement_points_value'] ) ) ) /src/applications/core/extensions/core/MemberFilter/Achievements.php:342:addToStack( 'member_filter_core_cheev_points_eq_desc', NULL, array( 'sprintf' => array( $filters['achievement_points_value'] ) ) ) /src/applications/core/extensions/core/MemberFilter/Achievements.php:352:addToStack( 'member_filter_core_cheev_desc', NULL, [\n 'sprintf' => [\n \IPS\Member::loggedIn()->language()->addToStack( 'recognize_badges_pluralize', NULL, ['sprintf' => [$count], 'pluralize' => [$count]] )\n ]] ) /src/applications/core/extensions/core/MemberFilter/Achievements.php:364:addToStack( 'member_filter_core_cheev_desc', NULL, [\n 'sprintf' => [\n \IPS\Member::loggedIn()->language()->addToStack( 'achievement_ranks_pluralize', NULL, ['sprintf' => [$count], 'pluralize' => [$count]] )\n ]] ) /src/applications/core/extensions/core/MemberFilter/Reputation.php:156:addToStack( 'member_filter_core_reputation_gt_desc', NULL, array( 'sprintf' => array( $filters['reputation_score'] ) ) ) /src/applications/core/extensions/core/MemberFilter/Reputation.php:159:addToStack( 'member_filter_core_reputation_lt_desc', NULL, array( 'sprintf' => array( $filters['reputation_score'] ) ) ) /src/applications/core/extensions/core/MemberFilter/Reputation.php:162:addToStack( 'member_filter_core_reputation_eq_desc', NULL, array( 'sprintf' => array( $filters['reputation_score'] ) ) ) /src/applications/core/extensions/core/MemberFilter/Joined.php:249:addToStack( 'member_filter_core_joined_range_desc', NULL, array( 'sprintf' => array( $start->localeDate(), $end->localeDate() ) ) ) /src/applications/core/extensions/core/MemberFilter/Joined.php:254:addToStack( 'member_filter_core_joined_days_desc', NULL, array( 'sprintf' => array( $filters['days'] ) ) ) /src/applications/core/extensions/core/MemberFilter/Joined.php:258:addToStack( 'member_filter_core_joined_days_lt_desc', NULL, array( 'sprintf' => array( $filters['days_lt'] ) ) ) /src/applications/core/extensions/core/MemberFilter/Content.php:156:addToStack( 'member_filter_core_content_gt_desc', NULL, array( 'sprintf' => array( $filters['content_count_score'] ) ) ) /src/applications/core/extensions/core/MemberFilter/Content.php:159:addToStack( 'member_filter_core_content_lt_desc', NULL, array( 'sprintf' => array( $filters['content_count_score'] ) ) ) /src/applications/core/extensions/core/MemberFilter/Content.php:162:addToStack( 'member_filter_core_content_eq_desc', NULL, array( 'sprintf' => array( $filters['content_count_score'] ) ) ) /src/applications/core/extensions/core/MemberFilter/Group.php:97:addToStack( 'member_filter_core_group_desc', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $humanGroups ) ) ) ) /src/applications/core/modules/admin/applications/api.php:69:addToStack('api_blocked_file', NULL, array( 'sprintf' => array( \IPS\ROOT_PATH ) ) ) /src/applications/core/modules/front/members/profile.php:2044:addToStack('recognize_points_so_far_both', NULL, [\n 'sprintf' => [\n $this->member->name,\n\IPS\Member::loggedIn()->language()->addToStack('recognize_points_pluralize', NULL, [ 'sprintf' => [ $this->member->todaysRecognizePoints() ], 'pluralize' => [ $this->member->todaysRecognizePoints() ] ]),\n \IPS\Member::loggedIn()->language()->addToStack('recognize_badges_pluralize', NULL, [ 'sprintf' => [ $this->member->todaysRecognizeBadges() ], 'pluralize' => [ $this->member->todaysRecognizeBadges() ] ]),\n ] ] ) /src/applications/core/modules/front/members/profile.php:2053:addToStack('recognize_points_so_far_single', NULL, [\n 'sprintf' => [\n $this->member->name,\n\IPS\Member::loggedIn()->language()->addToStack('recognize_points_pluralize', NULL, [ 'sprintf' => [ $this->member->todaysRecognizePoints() ], 'pluralize' => [ $this->member->todaysRecognizePoints() ] ])\n ] ] ) /src/applications/core/modules/front/members/profile.php:2061:addToStack('recognize_points_so_far_single', NULL, [\n 'sprintf' => [\n $this->member->name,\n\IPS\Member::loggedIn()->language()->addToStack('recognize_points_pluralize', NULL, [ 'sprintf' => [ $this->member->todaysRecognizeBadges() ], 'pluralize' => [ $this->member->todaysRecognizeBadges() ] ])\n ] ] ) /src/applications/core/modules/front/members/profile.php:2075:addToStack( 'recognize_points_so_far_limit', NULL, ['sprintf' => [$this->member->name, $maxPoints]] ) /src/applications/core/modules/front/members/profile.php:2079:addToStack( 'recognize_points_so_far_limit_none', NULL, ['sprintf' => [$this->member->name]] ) /src/applications/core/modules/front/members/profile.php:2115:addToStack( 'recognize_message__desc', NULL, [ 'sprintf' => [ $content->author()->name ] ] ) /src/applications/core/modules/front/members/profile.php:2137:addToStack('recognize_author', NULL, [ 'sprintf' => [ $content->author()->name ] ] ) /src/applications/core/modules/front/discover/streams.php:637:addToStack( 'stream_narrow_by_container_label', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $containerClass::$nodeTitle ) ) ) ) /src/applications/core/extensions/core/MemberACPProfileBlocks/Points.php:49:addToStack('acp_profile_edit_points', NULL, [ 'sprintf' => $this->member->name ] ) /src/applications/core/modules/front/discover/popular.php:151:addToStack( 'leaderboard_history_desc', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( 'leaderboard_history_likes') ) ) ) /src/applications/core/modules/front/discover/popular.php:156:addToStack( 'leaderboard_history_desc', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( 'leaderboard_history_rep') ) ) ) /src/applications/core/modules/front/discover/popular.php:330:addToStack( 'leaderboard_in_app', NULL, array( 'sprintf' => array( $areas[ \IPS\Request::i()->in ][ 1 ] ) ) ) /src/applications/core/modules/front/discover/popular.php:481:addToStack( ( $date->localeDate() == $endDate->localeDate() ) ? $popularResultsSingle : $popularResultsMany, NULL, array( 'sprintf' => array( $date->localeDate(), $descApp ) ) ) /src/applications/core/modules/admin/members/members.php:1376:addToStack( 'force_password_reset_member', NULL, array( 'sprintf' => array( $member->acpUrl()->csrf()->setQueryString( 'do', 'forcePassReset' ), \IPS\Member::loggedIn()->language()->addToStack( 'force_password_reset_member_confirmmsg' ) ) ) ) /src/applications/core/modules/admin/members/members.php:1503:addToStack( 'force_password_reset_member_external', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $externals ) ) ) ) /src/applications/core/modules/admin/members/members.php:4604:addToStack( 'acp_badge_from_recognize', NULL, [ 'sprintf' => [ $recognize->content()->url(), $recognize->content()->indefiniteArticle() ] ] ) /src/applications/core/sources/Promote/Promote.php:398:addToStack( 'num_replies', NULL, array( 'pluralize' => array( $count ) ) ) /src/applications/core/sources/Promote/Promote.php:405:addToStack( 'num_reviews', NULL, array( 'pluralize' => array( $count ) ) ) /src/applications/core/sources/Promote/Promote.php:421:addToStack( $object->_countLanguageString, NULL, array( 'pluralize' => array( $count ) ) ) /src/applications/core/sources/Promote/Promote.php:459:addToStack( 'promote_metadescription_container', NULL, array(\n 'htmlsprintf' => array( $author->link(), $this->objectDatePosted->html( FALSE ) ),\n 'sprintf' => array( $object->indefiniteArticle(), $container->url(), $container->_title ),\n ) ) /src/applications/core/sources/Promote/Promote.php:466:addToStack( 'promote_metadescription_container_nolink', NULL, array(\n 'sprintf' => array( $object->indefiniteArticle( $plaintextLanguage ), $container->getTitleForLanguage( $plaintextLanguage ), $author->name, $this->objectDatePosted->relative( \IPS\DateTime::RELATIVE_FORMAT_NORMAL, $plaintextLanguage ) ),\n ) ) /src/applications/core/sources/Promote/Promote.php:475:addToStack( 'promote_metadescription_nocontainer', NULL, array(\n 'htmlsprintf' => array( $author->link(), $this->objectDatePosted->html( FALSE ) ),\n 'sprintf' => array( $object->indefiniteArticle() )\n ) ) /src/applications/core/sources/Promote/Promote.php:482:addToStack( 'promote_metadescription_nocontainer', NULL, array(\n 'sprintf' => array( $object->indefiniteArticle( $plaintextLanguage ), $author->name, $this->objectDatePosted->relative( \IPS\DateTime::RELATIVE_FORMAT_NORMAL, $plaintextLanguage ) )\n ) ) /src/applications/core/sources/Promote/Promote.php:492:addToStack( 'promote_metadescription_nocontainer', NULL, array(\n 'htmlsprintf' => array( $author->link(), $this->objectDatePosted->html( FALSE ) ),\n 'sprintf' => array( $object->indefiniteArticle() )\n ) ) /src/applications/core/sources/Promote/Promote.php:499:addToStack( 'promote_metadescription_nocontainer', NULL, array(\n 'sprintf' => array( $object->indefiniteArticle( $plaintextLanguage ), $author->name, $this->objectDatePosted->relative( \IPS\DateTime::RELATIVE_FORMAT_NORMAL, $plaintextLanguage ) )\n ) ) /src/applications/core/sources/Promote/Promote.php:508:addToStack( 'promote_metadescription_node', NULL, array(\n 'htmlsprintf' => array( $author->link() ),\n 'sprintf' => array( $object->url(), $object->_title )\n ) ) /src/applications/core/sources/Promote/Promote.php:515:addToStack( 'promote_metadescription_node_nolink', NULL, array(\n 'sprintf' => array( $object->getTitleForLanguage( $plaintextLanguage ), $author->name )\n ) ) /src/applications/core/sources/Promote/Promote.php:982:addToStack( 'promote_thing_in_thing', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $object::$title ), $object->item()->mapped('title') ) ) ) /src/applications/cms/widgets/Database.php:70:addToStack( 'cms_db_in_use_by_page', NULL, array( 'sprintf' => array( $db->_title, $page->full_path ) ) ) /src/applications/core/sources/Warnings/Warning.php:348:addToStack('warn_cheeve_point_reduction__desc', NULL, [ 'sprintf' => [ $member->name, $member->achievements_points, $member->name ] ] ) /src/applications/core/sources/Profanity/Profanity.php:99:addToStack( 'profanity_filter_action_moderate_min_posts', NULL, array( 'pluralize' => array( $row['min_posts'] ) ) ) /src/applications/cms/modules/front/database/ajax.php:64:addToStack( 'cms_autocomplete_category', NULL, array( 'sprintf' => array( $record->container()->_title ) ) ) /src/applications/core/sources/Reports/Rules.php:345:addToStack( 'automoderation_points_needed_badge', NULL, array( 'pluralize' => array( $this->points_needed ) ) ) /src/applications/cms/modules/front/database/category.php:105:addToStack( 'cms_record_i_started_sprintf', NULL, array( 'sprintf' => $database->recordWord() ) ) /src/applications/cms/modules/front/database/category.php:123:addToStack( 'cms_record_i_started_sprintf', NULL, array( 'sprintf' => $database->recordWord() ) ) /src/applications/cms/modules/admin/pages/blocks.php:176:addToStack( 'cms_db_feed_block_with_name', NULL, array( 'sprintf' => array( $db->_title ) ) ) /src/applications/cms/modules/admin/pages/blocks.php:181:addToStack( 'cms_db_feed_block_with_name_disabled', NULL, array( 'sprintf' => array( $db->_title ) ) ) /src/applications/cms/modules/admin/pages/pages.php:310:addToStack( 'page_database_display', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack('content_db_' . static::$pageToDatabaseMap[ $page->id ] ) ) ) ) /src/applications/cms/sources/Pages/Page.php:1557:addToStack('content_editing_page', NULL, array( 'sprintf' => array( $this->_title ) ) ) /src/applications/core/modules/admin/customization/themes.php:1936:addToStack('theme_export_designer_mode_build_message', NULL, array( 'sprintf' => array( $url ) ) ) /src/applications/cms/modules/admin/pages/templates.php:333:addToStack('cms_theme_designer_mode_warning', NULL, array( 'sprintf' => array( $link ) ) ) /src/applications/cms/modules/admin/pages/templates.php:1000:addToStack( 'cms_database_template_used_in', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $names ) ) ) ) /src/applications/cms/sources/Blocks/Block.php:721:addToStack('content_block_block_editing', NULL, array( 'sprintf' => array( $this->_title ) ) ) /src/applications/cms/modules/admin/pages/media.php:122:addToStack('cms_media_designer_mode_warning', NULL, array( 'sprintf' => array( $link ) ) ) /src/applications/core/sources/Rss/Import.php:275:addToStack( 'rss_import_auto_follow_lang', NULL, array( 'sprintf' => array( $class::_definiteArticle() ) ) ) /src/applications/cms/modules/admin/databases/databases.php:1557:addToStack('cms_editing_database', NULL, array( 'sprintf' => array( $current->_title ) ) ) /src/applications/cms/modules/admin/databases/databases.php:1602:addToStack( 'database_use_categories_impossible', NULL, array( 'sprintf' => array( $current->numberOfCategories() ) ) ) /src/applications/cms/modules/admin/databases/databases.php:1884:addToStack( 'database_forum_record__rebuild', NULL, array( 'sprintf' => array( $rebuildUrl ) ) ) /src/applications/cms/modules/admin/databases/databases.php:1885:addToStack( 'database_forum_comments__rebuild', NULL, array( 'sprintf' => array( $rebuildUrlCounts ) ) ) /src/applications/cms/extensions/core/Queue/MoveSingleRecord.php:183:addToStack( 'moving_database_comments_single', NULL, array( 'sprintf' => array( $record->url(), $record->mapped('title') ) ) ) /src/applications/cms/extensions/core/Queue/MoveComments.php:138:addToStack( 'moving_database_comments', NULL, array( 'sprintf' => array( $database->_title ) ) ) /src/applications/cms/sources/Fields/Fields.php:1483:addToStack('field_daterange_start_end', NULL, array( 'sprintf' => array( $start, $end ) ) ) /src/applications/cms/sources/Fields/Fields.php:1487:addToStack('field_daterange_start', NULL, array( 'sprintf' => array( $start ) ) ) /src/applications/cms/sources/Fields/Fields.php:1491:addToStack('field_daterange_end', NULL, array( 'sprintf' => array( $end ) ) ) /src/applications/cms/sources/Fields/Fields.php:1721:addToStack( 'cms_field_no_type_warning', NULL, array( 'sprintf' => array( $this->type ) ) ) /src/applications/cms/sources/Fields/Fields.php:1854:addToStack( 'cms_db_relational_with_name_disabled', NULL, array( 'sprintf' => array( $db->_title ) ) ) /src/applications/gallery/sources/Album/Album.php:1467:addToStack("gallery_album_{$this->id}_desc", NULL, array( 'removeLazyLoad' => true ) ) /src/applications/cms/sources/Categories/Categories.php:1127:addToStack( static::$titleLangPrefix . $indexData['index_container_id'], NULL, $escape ? array( 'escape' => $escape ) : array() ) /src/applications/cms/sources/Categories/Categories.php:1201:addToStack( 'content_db_' . $this->database_id, NULL, $options ) /src/applications/calendar/sources/Venue/Venue.php:259:addToStack('calendar_venue_' . $this->id . '_desc', NULL, array( 'removeLazyLoad' => true ) ) /src/applications/nexus/modules/admin/reports/markets.php:57:addToStack( 'nexus_report_income', NULL, array( 'sprintf' => array( $currency ) ) ) /src/applications/nexus/modules/admin/reports/income.php:66:addToStack( 'nexus_report_income_by_member', NULL, array( 'sprintf' => array( $currency ) ) ) /src/applications/nexus/modules/admin/reports/income.php:72:addToStack( 'nexus_report_income_by_method', NULL, array( 'sprintf' => array( $currency ) ) ) /src/applications/nexus/extensions/core/MemberHistory/Nexus.php:717:addToStack( 'nexus_history_cancelrenewals_with_name', NULL, array( 'sprintf' => array( \IPS\nexus\Subscription\Package::load( $val['id'] )->_title, $byCustomer ) ) ) /src/applications/nexus/extensions/core/MemberHistory/Nexus.php:721:addToStack( 'nexus_history_cancelrenewals', NULL, array( 'sprintf' => array( $byCustomer ) ) ) /src/applications/nexus/extensions/core/MemberHistory/Nexus.php:732:addToStack( 'nexus_history_subscription_changed', NULL, array( 'sprintf' => array( ( isset( $val['system'] ) and $val['system'] ) ? '' : $byStaff ) ) ) /src/applications/nexus/sources/Support/Request.php:1029:addToStack( 'note_added_sent_to', NULL, array( 'sprintf' => $newDepartment->_title ) ) /src/applications/nexus/sources/Purchase/Purchase.php:146:addToStack( 'plus_tax_rate', NULL, array( 'sprintf' => array( $this->renewals->tax->_title ) ) ) /src/applications/nexus/sources/Subscription/Subscription.php:132:addToStack( 'nexus_subs_subscribed_with_expire' . ( ( $this->purchase and $this->purchase->renewals ) ? '' : '_no_renewal' ), NULL, array( 'sprintf' => array( $this->_expire->dayAndMonth() . ' ' . $this->_expire->format('Y') ) ) ) /src/applications/downloads/sources/Category/Category.php:355:addToStack( 'downloadcategory_topic_rebuild', NULL, array( 'sprintf' => array( $rebuildUrl ) ) ) /src/system/Content/Content.php:1997:addToStack( 'promote_metadescription_container', NULL, array(\n 'htmlsprintf' => array( $author->link(), \IPS\DateTime::ts( $this->mapped('date') )->html( FALSE ) ),\n 'sprintf' => array( $object->indefiniteArticle(), $container->url(), $container->_title ),\n ) ) /src/system/Content/Content.php:2004:addToStack( 'promote_metadescription_container_nolink', NULL, array(\n 'sprintf' => array( $object->indefiniteArticle( $plaintextLanguage ), $container->getTitleForLanguage( $plaintextLanguage ), $author->name, \IPS\DateTime::ts( $this->mapped('date') ) ),\n ) ) /src/system/Content/Content.php:2013:addToStack( 'promote_metadescription_nocontainer', NULL, array(\n 'htmlsprintf' => array( $author->link(), \IPS\DateTime::ts( $this->mapped('date') )->html( FALSE ) ),\n 'sprintf' => array( $object->indefiniteArticle() )\n ) ) /src/system/Content/Content.php:2020:addToStack( 'promote_metadescription_nocontainer', NULL, array(\n 'sprintf' => array( $object->indefiniteArticle( $plaintextLanguage ), $author->name, \IPS\DateTime::ts( $this->mapped('date') ) )\n ) ) /src/system/Content/Content.php:2030:addToStack( 'promote_metadescription_nocontainer', NULL, array(\n 'htmlsprintf' => array( $author->link(), \IPS\DateTime::ts( $this->mapped('date') )->html( FALSE ) ),\n 'sprintf' => array( $object->indefiniteArticle() )\n ) ) /src/system/Content/Content.php:2037:addToStack( 'promote_metadescription_nocontainer', NULL, array(\n 'sprintf' => array( $object->indefiniteArticle( $plaintextLanguage ), $author->name, \IPS\DateTime::ts( $this->mapped('date') ) )\n ) ) /src/system/Member/Member.php:523:addToStack( 'guest_name_shown', NULL, array( 'sprintf' => array( $this->_data['name'] ) ) ) /src/system/Member/Member.php:5174:addToStack('leaderboard_tab_x', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack("{$commentClass::$title}_pl_lc") ) ) ) /src/system/Member/Member.php:5178:addToStack('leaderboard_tab_x', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack("{$item::$title}_pl_lc") ) ) ) /src/system/Node/Model.php:594:addToStack( static::$titleLangPrefix . $indexData['index_container_id'], NULL, $escape ? array( 'escape' => TRUE ) : array() ) /src/system/Node/Model.php:692:addToStack( static::$titleLangPrefix . $this->_id, NULL, array( 'escape' => TRUE ) ) /src/system/Node/Model.php:710:addToStack( static::$titleLangPrefix . $this->_id, NULL, $options ) /src/system/Content/Reactable.php:431:addToStack( '_defart_from_date', NULL, array( 'htmlsprintf' => array( $item->url(), $item->mapped('title'), \IPS\DateTime::ts( $date )->html() ) ) ) /src/system/Content/Promote/Facebook.php:168:addToStack( 'promote_facebook_page_prefix', NULL, array( 'sprintf' => array( $data['name'] ) ) ) /src/system/Content/Promote/Facebook.php:177:addToStack( 'promote_facebook_group_prefix', NULL, array( 'sprintf' => array( $data['name'] ) ) ) /src/system/Content/Recognizable.php:170:addToStack('recognize_blurb_main', NULL, [ 'htmlsprintf' => [ $this::$title, $this->recognized->_given_by->link() ] ] ) /src/system/Content/Recognizable.php:175:addToStack( 'recognize_blurb_awarded_points_and_badge', NULL, ['htmlsprintf' => [$this->recognized->badge()->_title, $this->recognized->points]] ) /src/system/Content/Recognizable.php:179:addToStack( 'recognize_blurb_awarded_points_and_badge_third', NULL, ['htmlsprintf' => [$this->author()->name, $this->recognized->badge()->_title, $this->recognized->points]] ) /src/system/Content/Recognizable.php:186:addToStack('recognize_blurb_awarded_points', NULL, [ 'sprintf' => [ $this->recognized->points ] ] ) /src/system/Content/Recognizable.php:190:addToStack('recognize_blurb_awarded_points_third', NULL, [ 'sprintf' => [ $this->author()->name, $this->recognized->points ] ] ) /src/system/Content/Recognizable.php:197:addToStack('recognize_blurb_awarded_badge', NULL, [ 'htmlsprintf' => [ $this->recognized->badge()->_title ] ] ) /src/system/Content/Recognizable.php:201:addToStack('recognize_blurb_awarded_badge_third', NULL, [ 'htmlsprintf' => [ $this->author()->name, $this->recognized->badge()->_title ] ] ) Generated with comby 'addToStack(:[1], NULL, :[2])' '' .php -match-only -d '/src' You can also use comby to automatically fix them! comby 'addToStack(:[1], NULL, :[2])' 'addToStack(:[1], false, :[2])' .php -d $PATH_TO_CODE
  20. To reproduce: Open "mark as solution" in a new tab several times Look at the author's solutions list, and observe that the same post shows up multiple times, and the user's solution count has increased accordingly. This can be triggered accidentally, eg if the user accidentally clicks the button multiple times or has a network dropout, and could also be exploited by users to quietly artificially boost their solution count. It happens because in \IPS\Content\Solvable::toggleSolveComment, you don't delete the existing entry from core_solved_index when marking a new post as a solution (though you do clear the solved flag from the existing post). You also don't remove the duplicate notifications. (This could also be solved with a unique index on core_solved_index on comment_class+item_id.)
  21. When a member is suspended, they currently don't receive any notifications (they get filtered out twice in Notification::send). This makes sense for email and push notifications while they are suspended, but I don't think it makes sense for in-platform notifications. The member is likely to want to catch up on them when they get back (especially after a short suspension), and there's no way other than notifications to find where you were quoted or mentioned.
  22. This means the new min version will be higher than the old max version, right? That's really unfortunate for orchestrating the upgrade.
  23. You've already shipped the fix as the change to applications/core/modules/front/system/ajax.php in 106143. There's some other related bugs that I'll file a ticket for, but upgrading to that patch release fixes my local repro. (It is because of the service worker.)
  24. If there are polls in the middle of the topic then yeah they could definitely get lost, but I would imagine polls like that to be pretty ephemeral anyway - if you want a poll that everyone sees, you add it to the first post (or recommend it).
  25. Fwiw I've also had several reports of this since upgrading to 4.6, acking the privacy policy or validating the account. There were no corresponding requests in the backend, and from the one person who gave me some more details after it worked it seemed like the request was just super delayed in the browser. My current hypothesis is that it's a service worker issue but I have nothing to corroborate that.