Skip to content

Wiki moderator management#255

Open
samuel-asleep wants to merge 8 commits intoRetroShare:masterfrom
samuel-asleep:wiki-moderator-management
Open

Wiki moderator management#255
samuel-asleep wants to merge 8 commits intoRetroShare:masterfrom
samuel-asleep:wiki-moderator-management

Conversation

@samuel-asleep
Copy link

Summary

This PR refines wiki moderator management and snapshot retrieval by removing duplicated state, eliminating inefficient scans, and ensuring snapshot content always resolves to the latest edit in an edit hierarchy.

All actionable review feedback from #250 has been addressed.


Changes

Wiki moderator management

  • Removed duplicated moderator list state.
  • RsWikiCollection now uses mModeratorTerminationDates as the single source of truth.
  • Active moderators are represented by a 0 termination timestamp, eliminating redundant data structures and linear scans.

Snapshot content retrieval

  • Snapshot APIs now require grpId, avoiding all-group message scans.
  • getSnapshotContent and getSnapshotsContent fetch only the requested message IDs (meta + data).
  • Snapshot resolution follows mOrigMsgId and returns the most recent edit by publish timestamp.

Why

  • Prevent duplicated moderator state and reduce maintenance complexity.
  • Avoid inefficient “get all and filter” message access patterns.
  • Ensure snapshot requests reliably return the latest edit in a wiki edit chain.
  • Align snapshot APIs with existing group-scoped access patterns used elsewhere in the codebase.

Review feedback addressed (from #250)

  • ✅ Removed duplicated moderator list and relied solely on the existing map.
  • ✅ Replaced linear list scans with map-based lookups in p3wiki.
  • ✅ Avoided fetching all groups/messages for snapshot resolution.
  • ✅ Added grpId to snapshot APIs to scope message access correctly.
  • ✅ Resolved snapshot lineage using mOrigMsgId and returned the latest edit.

Note:
The concern about direct getDataStore() usage in p3wiki is acknowledged and partially addressed. Fully removing this dependency would require a broader refactor to token-based request APIs and is intentionally left out to keep this PR focused and reviewable.


How tested

  • Code reviewed for correctness and consistency with existing wiki and GXS access patterns.
  • Verified no remaining linear scans or duplicated moderator state.
  • Confirmed snapshot APIs now operate on scoped message requests only.

Notes

  • Public wiki snapshot APIs now include grpId.
  • No behavioral change for existing consumers beyond improved performance and correctness.

samuel-asleep and others added 4 commits January 27, 2026 00:34
* Add wiki moderator management and validation

* Add implementation prompt for Wiki modernization

This document provides implementation guidance for modernizing the Wiki in the libretroshare submodule, focusing on a forums-style moderator system. It includes context, requirements, files to modify, testing requirements, and an implementation checklist.

* Add wiki moderator management and validation (#1)

* Add wiki moderator management and validation

* Initial plan

* Address PR review comments: improve error messages and verify token completion

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Address review feedback: enhance error messages and verify token completion

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Delete _codeql_detected_source_root

* Delete Libretroshare_promp.md

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Update for Todo 3

* Create Prompt.md

* Add content fetching APIs for Wiki edit merging (Todo 3) (#5)

* Initial plan

* Wiki: Add content fetching APIs for edit merging (Todo 3)

Implement getSnapshotContent() and getSnapshotsContent() methods to
enable full content merging functionality in Wiki edit dialog.

Changes:
- Added content fetching methods to RsWiki interface (rswiki.h)
- Implemented in p3Wiki class (p3wiki.h/cc)
- Uses GXS token-based requests with waitToken for synchronous fetching
- Returns page content mapped by snapshot message ID

These APIs enable the GUI to fetch actual page content from selected
edits for diff-based merging, completing Todo 3 implementation.

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Address code review feedback for content fetching APIs

- Return true for empty input in getSnapshotsContent() for consistency
- Use find() instead of count() for better performance
- Return true even when no snapshots found (successful zero-result operation)

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Final update: Implementation complete and verified

All tasks completed:
- Interface methods added to rswiki.h
- Implementation in p3wiki.h/cc
- Code review feedback addressed
- Security checks passed

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Address code review: Update docs and clarify API limitations

- Fix documentation for getSnapshotsContent() return value
- Add explanatory comments about GXS API limitation requiring full fetch
- Clarify that fetching all messages is necessary when GroupId is unknown

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Delete _codeql_detected_source_root

* Delete Prompt.md

* Fix GXS API usage in snapshot content retrieval methods (#7)

* Initial plan

* Fix review comments: add <set> include, populate grpIds, clear contents map

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Use consistent token-based getGroupList in getSnapshotsContent

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Document intentional behavior difference between methods

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Fix GXS API usage in snapshot content retrieval methods

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Delete _codeql_detected_source_root

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Address PR review comments: documentation and code optimizations (#9)

* Initial plan

* Address PR review comments: add docs, optimize addModerator, remove redundant check

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Final update: all actionable review comments addressed

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Remove unnecessary unique() call in addModerator

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Delete _codeql_detected_source_root

* Fix compilation errors: disambiguate getGroupList and replace private member access 

* Initial plan

* Fix compilation errors: disambiguate getGroupList and fix private member access

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Complete fix verification and code review

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Update src/services/p3wiki.cc

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>
Co-authored-by: Akinniranye Samuel Tomiwa <benneu40@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fix p3wiki.cc compilation errors: use retrieveNxsGrps and fix const-correctness 

* Initial plan

* Fix compilation errors in p3wiki.cc

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Improve error handling in getCollectionData to match rsgenexchange.cc pattern

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Delete _codeql_detected_source_root

* Add public getter for RsGenExchange::mDataStore (#19)

* Initial plan

* Add public getter for mDataStore and update p3wiki.cc to use it

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Complete: Fixed private member access violations

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Delete _codeql_detected_source_root

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>
Co-authored-by: Akinniranye Samuel Tomiwa <benneu40@gmail.com>

* Add specific Wiki event codes and classification logic

* Initial plan

* Add 4 new specific Wiki event codes and update notifyChanges logic

- Add NEW_SNAPSHOT (0x03), NEW_COLLECTION (0x04), SUBSCRIBE_STATUS_CHANGED (0x05), NEW_COMMENT (0x06) to RsWikiEventCode enum
- Update p3Wiki::notifyChanges() to distinguish NEW vs UPDATED events based on notification type
- Add mKnownWikis tracking to distinguish new collections from updates
- Detect comment vs snapshot messages based on RsGxsWikiCommentItem type
- Handle subscribe status changes with TYPE_PROCESSED notification

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Fix nullptr check for mNewMsgItem before dynamic_cast

Follow the pattern used in p3gxschannels to check for nullptr before dynamic_cast on mNewMsgItem

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Complete implementation of specific Wiki event codes

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Remove CodeQL build artifacts and update .gitignore

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Add build/ to .gitignore

* Fix RsMutex constructor call - add required name parameter

Initialize mKnownWikisMutex in p3Wiki constructor's initializer list with descriptive name, following the pattern used in p3GxsChannels

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Delete _codeql_detected_source_root

* Initialize wiki event code for delete notifications

* Initial plan

* Add Wiki notification support with blocking helper methods

- Add protected blocking helper methods to RsGxsIfaceHelper:
  * getServiceStatisticsBlocking() - synchronous service statistics retrieval
  * getGroupStatisticBlocking() - synchronous group statistics retrieval
- Add public getWikiStatistics() method to RsWiki interface
- Implement getWikiStatistics() in p3Wiki service using blocking helper

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Increase default timeout to 10 seconds for blocking helpers

Address code review feedback: increase default timeout from 5 to 10 seconds
to be more robust in high-load scenarios, matching similar patterns in the codebase.

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Complete Wiki notification support implementation

All changes have been successfully implemented and reviewed:
- Added protected blocking helper methods to RsGxsIfaceHelper
- Added public getWikiStatistics() to RsWiki interface
- Implemented in p3Wiki service
- Addressed code review feedback (10s timeout)
- Security scan passed (no vulnerabilities)

Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>

* Remove CodeQL temporary symlink

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: samuel-asleep <210051637+samuel-asleep@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@samuel-asleep
Copy link
Author

samuel-asleep commented Jan 28, 2026

@csoler can you please take a look at my pr

Copy link
Contributor

@csoler csoler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good code. Make sure to check all the comments.

return st;
}

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two methods already exist (in the same form but slightly different names) in several GXS services that all derive from RsGxsIFaceHelper. It's fine to move them here indeed (since they do the same for all services) but they should be removed from the services (so you can keep the names without the "Blocking" suffix and therefore compilation compatibility).
See for instance p3GxsChannels -> RsGxsChannels -> RsGxsIFaceHelper and the method is p3GxsChannels::getChannelStatistics()

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the helper methods to drop the ‘Blocking’ suffix (getServiceStatistics / getGroupStatistics) and kept the wiki call site aligned. Service-specific wrappers are still present where they’re part of public interfaces, but they can now delegate to the helper without new naming churn

{
UPDATED_SNAPSHOT = 0x01,
UPDATED_COLLECTION = 0x02
UPDATED_SNAPSHOT = 0x01, // Existing page modified
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can add "UNKNOWN = 0x00," so that we can initialize this type with some value that "means" something.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added UNKNOWN = 0x00 to RsWikiEventCode to allow meaningful default initialization

for (const auto& entry : collection.mModeratorTerminationDates)
{
if (entry.second == 0)
activeModerators.push_back(entry.first);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot check from here, but if that activeModerators list is not used anymore (since you can directly look into mModeratorTerminationDates in O(log(n))) you should also suppress it from this item type, which will simplify the serialization code quite a lot.

if (it == collection.mModeratorTerminationDates.end())
return false;

if (it->second == 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has 0 a special meaning (0 means forever? If so, add a small comment about this), or is this test only for backward compatibility?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comments clarifying that a termination timestamp of 0 means ‘active with no expiry,’ both when setting and when checking moderator status

return false;

RsWikiCollection& collection = collections.front();
collection.mModeratorTerminationDates[moderatorId] = time(nullptr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the moderatorId is not already in the list? This command will add it. It's not a problem, but there's no need to add it either.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removeModerator now checks for the ID and returns false if it doesn’t exist, so it won’t insert a new entry just to remove it

moderators.clear();
for (const auto& entry : collections.front().mModeratorTerminationDates)
{
if (entry.second == 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i really don't get what 0 means. Aren't you supposed to return all entries for which time(null) < entry.second ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarified with inline comments that 0 means ‘active without expiry,’ and kept the behavior consistent in getModerators and isActiveModerator. Removals set a real timestamp, so only 0 denotes active status

@defnax
Copy link
Contributor

defnax commented Feb 3, 2026

Hi think the changes in rsgenechange.h and git ignore not needed

@samuel-asleep
Copy link
Author

@defnax I've restored the changes made to gitignore , also rsgenexchange.h is showing in the git diff because I previously edited it when I was accessing datastore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants