{"id":338684,"date":"2026-07-18T11:04:18","date_gmt":"2026-07-18T11:04:18","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/pcio-vis-member-event\/"},"modified":"2026-10-19T17:38:18","modified_gmt":"2026-10-19T17:38:18","slug":"pcio-vis-member-event","status":"publish","type":"plugin","link":"https:\/\/tt.wordpress.org\/plugins\/pcio-vis-member-event\/","author":20981457,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.6.0","stable_tag":"1.6.0","tested":"7.1.4","requires":"6.2","requires_php":"8.1","requires_plugins":null,"header_name":"PCIO VIS Member Event","header_author":"PCIO","header_description":"Manages members and events for a voluntary organization.","assets_banners_color":"000000","last_updated":"2026-10-19 17:38:18","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/www.pcio.dk\/","header_author_uri":"https:\/\/www.pcio.dk","rating":0,"author_block_rating":0,"active_installs":0,"downloads":600,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"pcio","date":"2026-07-18 11:04:07","revision":3612536},"1.1.0":{"tag":"1.1.0","author":"pcio","date":"2026-08-15 14:38:59","revision":3648718},"1.2.0":{"tag":"1.2.0","author":"pcio","date":"2026-08-18 13:16:05","revision":3652942},"1.3.0":{"tag":"1.3.0","author":"pcio","date":"2026-09-07 17:23:01","revision":3685324},"1.4.0":{"tag":"1.4.0","author":"pcio","date":"2026-09-11 11:53:41","revision":3691329},"1.5.0":{"tag":"1.5.0","author":"pcio","date":"2026-10-02 19:43:05","revision":3725396},"1.6.0":{"tag":"1.6.0","author":"pcio","date":"2026-10-19 17:38:18","revision":3755275}},"upgrade_notice":{"1.6.0":"<p><strong>Back up your database before updating.<\/strong> 1.6.0 Introduces new history and undo functionality so you need to upgrade all the extension plugins.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3614379,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3614379,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3612529,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3612529,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.0","1.1.0","1.2.0","1.3.0","1.4.0","1.5.0","1.6.0"],"block_files":[],"assets_screenshots":{"screenshot-1.jpg":{"filename":"screenshot-1.jpg","revision":3612529,"resolution":"1","location":"assets","locale":"","width":1344,"height":835},"screenshot-2.jpg":{"filename":"screenshot-2.jpg","revision":3612529,"resolution":"2","location":"assets","locale":"","width":1344,"height":993},"screenshot-3.jpg":{"filename":"screenshot-3.jpg","revision":3612529,"resolution":"3","location":"assets","locale":"","width":1343,"height":874},"screenshot-4.jpg":{"filename":"screenshot-4.jpg","revision":3612529,"resolution":"4","location":"assets","locale":"","width":1361,"height":1029}},"screenshots":{"1":"Landing page with tiles for each section in the application","2":"The calendar page","3":"Member list page.","4":"Vis admin menues in WordPress admin section."}},"plugin_section":[],"plugin_tags":[9054,1486,1919,455,1554],"plugin_category":[40,42,58],"plugin_contributors":[265152],"plugin_business_model":[],"class_list":["post-338684","plugin","type-plugin","status-publish","hentry","plugin_tags-documents","plugin_tags-events","plugin_tags-members","plugin_tags-newsletter","plugin_tags-signup","plugin_category-calendar-and-events","plugin_category-contact-forms","plugin_category-user-management","plugin_contributors-pcio","plugin_committers-pcio"],"banners":{"banner":"https:\/\/ps.w.org\/pcio-vis-member-event\/assets\/banner-772x250.png?rev=3612529","banner_2x":"https:\/\/ps.w.org\/pcio-vis-member-event\/assets\/banner-1544x500.png?rev=3612529","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/pcio-vis-member-event\/assets\/icon-128x128.png?rev=3614379","icon_2x":"https:\/\/ps.w.org\/pcio-vis-member-event\/assets\/icon-256x256.png?rev=3614379","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/pcio-vis-member-event\/assets\/screenshot-1.jpg?rev=3612529","caption":"Landing page with tiles for each section in the application"},{"src":"https:\/\/ps.w.org\/pcio-vis-member-event\/assets\/screenshot-2.jpg?rev=3612529","caption":"The calendar page"},{"src":"https:\/\/ps.w.org\/pcio-vis-member-event\/assets\/screenshot-3.jpg?rev=3612529","caption":"Member list page."},{"src":"https:\/\/ps.w.org\/pcio-vis-member-event\/assets\/screenshot-4.jpg?rev=3612529","caption":"Vis admin menues in WordPress admin section."}],"raw_content":"<!--section=description-->\n<p>PCIO VIS Member Event turns your WordPress site into a complete back office for a voluntary organization or association.<\/p>\n\n<p>It provides a members register, an event calendar with public sign-ups, e-mail and newsletter tools, a document library and a simple finance journal \u2014 all built on native WordPress capabilities so access control works with the roles you already use.<\/p>\n\n<p>Public pages are served automatically under <code>\/vis\/<\/code>; no WordPress pages or shortcodes are required to get started. The public event permalinks are the one exception: the plugin creates a single <strong>Event<\/strong> page (slug <code>event<\/code>, containing the <code>[pcio_me_event]<\/code> shortcode) that <code>\/events\/{slug}\/<\/code> links to, so those pages render through the theme's page template and use your site's header and footer:<\/p>\n\n<ul>\n<li><strong>Home<\/strong> (<code>\/vis\/<\/code>) \u2014 landing page for members.<\/li>\n<li><strong>Members<\/strong> (<code>\/vis\/members<\/code>) \u2014 searchable members directory.<\/li>\n<li><strong>Member<\/strong> (<code>\/vis\/members\/{id}<\/code>) \u2014 member detail page with a Settings tab and, for administrators, a History tab.<\/li>\n<li><strong>Calendar<\/strong> (<code>\/vis\/calendar<\/code>) \u2014 upcoming events with sign-up.<\/li>\n<li><strong>Event<\/strong> (<code>\/vis\/events\/{id}<\/code>) \u2014 member event detail page with banner, description and sign-up box.<\/li>\n<li><strong>Public event<\/strong> (<code>\/events\/{slug}<\/code>) \u2014 indexable, no-login event page rendered through the Event page.<\/li>\n<li><strong>Mails<\/strong> (<code>\/vis\/mails<\/code>) \u2014 member mailings.<\/li>\n<li><strong>Newsletters<\/strong> (<code>\/vis\/newsletters<\/code>) \u2014 public newsletter archive.<\/li>\n<li><strong>Documents<\/strong> (<code>\/vis\/documents<\/code>) \u2014 document library grouped by type.<\/li>\n<li><strong>Finance<\/strong> (<code>\/vis\/finance<\/code>) \u2014 finance overview for authorized users.<\/li>\n<li><strong>Statistics<\/strong> (<code>\/vis\/statistics<\/code>) \u2014 member growth chart, page-view and login counters, and sponsor link click tracking.<\/li>\n<\/ul>\n\n<p><strong>Roles and capabilities.<\/strong> The plugin defines a cumulative set of logical roles \u2014 Member, Volunteer, Event Manager, Publisher and System Admin \u2014 that grant additional capabilities to Vis members. Define these roles under <strong>Vis \u2192 Roles<\/strong> and assign them to member records. Default management capabilities are also granted to built-in WordPress roles.<\/p>\n\n<p>The <code>vis_member<\/code> capability is a separate membership marker granted to individual WordPress accounts when they are linked or provisioned as Vis members, and revoked when membership is removed. It is shown on <strong>Vis \u2192 Roles<\/strong> for reference, but is not selectable as a role capability. The default Member role intentionally has no additional capabilities; basic member access comes from this marker.<\/p>\n\n<p>In member lists, the Vis Role column displays <strong>Member<\/strong> when the linked WordPress account has <code>vis_member<\/code> and no explicit Vis role is assigned. This is a display-only fallback: stored assignments and permissions are unchanged, and explicitly assigned roles remain visible. It is not a subscription-status indicator.<\/p>\n\n<p>Additional role capabilities include:<\/p>\n\n<ul>\n<li>Volunteer<\/li>\n<li>Create own events<\/li>\n<li>Edit own events<\/li>\n<li>Edit all events<\/li>\n<li>Publish events \/ manage documents<\/li>\n<li>Manage members<\/li>\n<li>Manage settings &amp; mails<\/li>\n<li>Manage finance<\/li>\n<\/ul>\n\n<p><strong>Shortcodes.<\/strong> For themes that prefer to embed content in ordinary pages, the plugin also ships shortcodes: <code>[pcio_me_members]<\/code>, <code>[pcio_me_documents]<\/code>, <code>[pcio_me_edit_profile]<\/code>, <code>[pcio_me_event]<\/code>, <code>[pcio_me_events]<\/code>, <code>[pcio_me_event_roller]<\/code>, <code>[pcio_me_newsletters]<\/code>, <code>[pcio_me_member_count]<\/code>, <code>[pcio_me_member_signup]<\/code> and <code>[pcio_me_rolling_text]<\/code>. The <code>[pcio_me_event]<\/code> shortcode renders a compact event card when given <code>id<\/code> or <code>slug<\/code>, and the full-width detail layout when left bare on the Event page (where it resolves the event from <code>\/events\/{slug}\/<\/code>). The <code>[pcio_me_edit_profile]<\/code> shortcode renders a pre-filled profile form (membership number, name, email, phone, address) for the logged-in member, validates in the browser and saves over REST; the membership number is shown read-only. The <code>[pcio_me_members]<\/code> shortcode renders a searchable member table \u2014 it requires a logged-in account holding the <code>vis_member<\/code> capability, and the table includes each member's email address and phone number. The search box filters rows live and shows a message when nothing matches, and <code>?member_number=\u2026<\/code> in the page URL pre-fills the membership-number filter. The <code>[pcio_me_member_count]<\/code> shortcode displays an animated total member count and can be used in any page builder (e.g. a Divi Text Module); each render increments the front-page-view counter visible on the Statistics dashboard. The <code>[pcio_me_member_signup]<\/code> shortcode renders a public member registration form that creates the member, provisions a WordPress account and sends an automated welcome email. The <code>[pcio_me_rolling_text]<\/code> shortcode outputs a scheduled scrolling banner. A <strong>Shortcode reference<\/strong> page in the Vis admin documents every shortcode with its attributes and examples, and a <strong>Sponsor Links<\/strong> page documents the <code>pcio-sponsor-link<\/code> class used for click tracking.<\/p>\n\n<p><strong>Change history and undo.<\/strong> Every change to Vis data (members, events, sign-ups, roles, workgroups, documents, mails, journal entries and extension data) is logged with the time, the user and a readable description. <strong>Vis \u2192 History<\/strong> lists all changes newest first, and the event and member pages have their own History tab. Users with the <code>vis_undo_changes<\/code> capability (System Admin) can undo a change; the undo is itself logged and can be undone again. Undo restores database rows only \u2014 sent e-mails, payments and WordPress accounts are not reverted, and changes with such side effects are logged as not undoable. Long text fields (event descriptions, mail content) are summarised by length in the log instead of storing every version in full.<\/p>\n\n<p><strong>WordPress user cleanup.<\/strong> <strong>Vis \u2192 WP User Cleanup<\/strong> lists WordPress accounts not linked to a Vis member. WordPress administrators with user-deletion permissions can select accounts and choose <strong>Delete selected<\/strong>, or use <strong>Delete All<\/strong> for the eligible accounts on this list only. Accounts with any authored posts, pages, attachments, custom post types or revisions (including drafts and trash), registered-user comments, or legacy WordPress links are kept and marked in the Status column. Administrators, accounts with <code>manage_options<\/code>, super administrators and the current account are protected. The server rechecks eligibility when the action is submitted, uses WordPress user-deletion APIs and reports deleted, skipped and failed accounts. Deletion is permanent and unavailable on Multisite, where accounts and content may belong to other sites.<\/p>\n\n<p><strong>Unsaved dialog changes.<\/strong> Data-entry dialogs in the core, boat and products extensions stay open when clicking outside them. Cancel, the close button and Escape ask before discarding changed fields, including checkboxes, selected files and rich text. Navigating away with unsaved dialog changes triggers the browser's standard warning. Successful saves and checkout redirects do not trigger a discard warning. This protection keeps edits in the current page only; it does not store drafts or recover data after a browser crash.<\/p>\n\n<p><strong>System event types.<\/strong> The Event Types tab at <code>\/vis\/calendar\/#event-types<\/code> includes seeded Cafe, Art (art exhibition) and Holiday (all closed) types with stable slugs <code>cafe<\/code>, <code>art<\/code> and <code>holiday<\/code>. These types can be edited but cannot be deleted, and their system flag and slugs cannot be changed through the event-type API. New Cafe and Art types have <code>in_cafe_time<\/code> enabled; Holiday and user-created types default to disabled. The checkbox stores whether events may take place during cafe opening hours; warning suppression and closure behavior will be implemented separately. Upgrades reuse existing matching slugs, preserving their IDs, names and settings while marking them as system types.<\/p>\n\n<p>Regression tests live outside the plugin in the repository's <code>tests\/pcio-vis-member-event\/<\/code> folder and are not part of the distributed plugin. Run the PHP tests with <code>php tests\/pcio-vis-member-event\/&lt;name&gt;.test.php<\/code>. For the browser tests, serve the repository root locally and open <code>tests\/pcio-vis-member-event\/modal-guard.html<\/code>; its application fixtures mock REST requests and do not change WordPress data.<\/p>\n\n<p><strong>Extensions.<\/strong> PCIO VIS Member Event is the core plugin for a family of optional add-ons (gallery, products, tickets, project management, home-owner association tools and Conventus sync). The bundled Dompdf and php-qrcode libraries are shared with those extensions for PDF and QR generation.<\/p>\n\n<p><strong>Google Calendar sync.<\/strong> The optional <strong>Sync Google Calendar<\/strong> job (Vis \u2192 Sync Jobs, disabled by default) pushes Vis events from one year back to one year ahead into one or more Google calendars. Each calendar takes either all events (including unpublished drafts) or only homepage-approved events whose event type has public access. Configure it under <strong>Vis \u2192 Google<\/strong>: paste a Google Cloud service-account key (JSON, Calendar API enabled), share each Google calendar with the service-account e-mail (\"Make changes to events\"), and add the calendars. The job creates missing Google events, updates changed ones (a failed update is retried on the next run) and deletes Google events in the window that no longer match a Vis event. Chosen event types can be shown as single-day events ending at 22:00, so long-running events such as exhibitions do not fill the calendar.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>pcio-vis-member-event<\/code> directory to <code>\/wp-content\/plugins\/<\/code>.<\/li>\n<li>Activate the plugin in <strong>Plugins<\/strong> in the WordPress admin.<\/li>\n<li>Define event-system roles under <strong>Vis \u2192 Roles<\/strong> and assign them to member records.<\/li>\n<li>Configure plugin options under the <strong>Vis<\/strong> admin menu.<\/li>\n<\/ol>\n\n<p>After activation, if <code>\/vis\/<\/code> or an event page returns a 404, go to <strong>Settings \u2192 Permalinks<\/strong> and click <strong>Save Changes<\/strong> to flush the rewrite rules.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"where%20are%20the%20public%20pages%3F\"><h3>Where are the public pages?<\/h3><\/dt>\n<dd><p>The plugin registers pages automatically under <code>\/vis\/<\/code> \u2014 for example <code>https:\/\/yoursite.com\/vis\/members<\/code> and <code>https:\/\/yoursite.com\/vis\/calendar<\/code>. Individual events for members are available at <code>https:\/\/yoursite.com\/vis\/events\/{id}<\/code>, and the public, no-login event pages at <code>https:\/\/yoursite.com\/events\/{slug}<\/code>. On activation the plugin also creates a single <strong>Event<\/strong> page (slug <code>event<\/code>, containing the <code>[pcio_me_event]<\/code> shortcode); the public event URLs are rewritten onto that page so they render with your theme's header and footer. Do not delete it \u2014 it is recreated the next time you open the WordPress admin.<\/p><\/dd>\n<dt id=\"how%20do%20i%20control%20who%20can%20do%20what%3F\"><h3>How do I control who can do what?<\/h3><\/dt>\n<dd><p>Access is based on native WordPress capabilities. Open <strong>Vis \u2192 Roles<\/strong> to define the additional capabilities granted by each logical role, then assign a role to the member record. The logical roles (Member, Volunteer, Event Manager, Publisher, System Admin) are cumulative \u2014 each level includes the capabilities of the level below. Membership itself is controlled separately by the per-account <code>vis_member<\/code> marker; an empty Member role is normal and does not mean the member lacks access.<\/p><\/dd>\n<dt id=\"a%20public%20page%20returns%20a%20404.\"><h3>A public page returns a 404.<\/h3><\/dt>\n<dd><p>Go to <strong>Settings \u2192 Permalinks<\/strong> and click <strong>Save Changes<\/strong> to flush the rewrite rules. This registers the <code>\/vis\/<\/code> routes.<\/p><\/dd>\n<dt id=\"does%20this%20require%20any%20third-party%20services%3F\"><h3>Does this require any third-party services?<\/h3><\/dt>\n<dd><p>No. The core plugin runs entirely on your WordPress site. Optional extensions (such as ticket sales) may integrate with external services.<\/p>\n\n<p>The only exception is the optional Google Calendar sync, which is off until you store a Google key under <strong>Vis \u2192 Google<\/strong> and enable the \"Sync Google Calendar\" job. When it runs, it sends the title, plain-text description (or the shorter resume when the description is too long for Google), location and start\/end times of the synced events to Google: tokens from <code>https:\/\/oauth2.googleapis.com\/token<\/code>, and calendar data to the Google Calendar API at <code>https:\/\/www.googleapis.com\/calendar\/v3<\/code>. See Google's <a href=\"https:\/\/policies.google.com\/terms\">Terms of Service<\/a> and <a href=\"https:\/\/policies.google.com\/privacy\">Privacy Policy<\/a>.<\/p><\/dd>\n<dt id=\"how%20long%20can%20changes%20be%20undone%3F\"><h3>How long can changes be undone?<\/h3><\/dt>\n<dd><p>Undo data is kept for 180 days; after that the change stays in the history but can no longer be undone. Developers can change the period with the <code>pcio_me_history_undo_retention_days<\/code> filter. A change can only be undone while the affected rows are unchanged \u2014 undo newer changes first.<\/p><\/dd>\n<dt id=\"which%20hooks%20does%20the%20change%20history%20offer%3F\"><h3>Which hooks does the change history offer?<\/h3><\/dt>\n<dd><ul>\n<li><code>pcio_me_history_tables<\/code> (filter) \u2014 register an extension table so its writes are logged and undoable. Write through <code>PCIO_VIS_History::insert()<\/code>, <code>update()<\/code>, <code>delete()<\/code> and <code>upsert()<\/code> instead of <code>$wpdb<\/code>.<\/li>\n<li><code>pcio_me_history_undo_retention_days<\/code> (filter) \u2014 days to keep undo data (default 180).<\/li>\n<li><code>pcio_me_history_undone<\/code> (action) \u2014 fires after an undo with the history id, the history row and the applied steps; use it to re-apply side effects outside the database tables.<\/li>\n<li><code>pcio_me_member_tabs<\/code> (filter) \u2014 add tabs to the member detail page, like <code>pcio_me_event_tabs<\/code> does for events.<\/li>\n<\/ul><\/dd>\n<dt id=\"how%20do%20i%20uninstall%3F\"><h3>How do I uninstall?<\/h3><\/dt>\n<dd><p>Deactivate and delete the plugin from the WordPress admin \u2014 the plugin drops its own tables and options on delete, so this is only needed if you remove the plugin files by hand. To also remove the data, open your database admin tool and drop these tables (replace <code>wp_<\/code> with your actual table prefix):<\/p>\n\n<ul>\n<li><code>wp_me_members<\/code><\/li>\n<li><code>wp_me_member_meta<\/code><\/li>\n<li><code>wp_me_member_roles<\/code><\/li>\n<li><code>wp_me_vis_roles<\/code><\/li>\n<li><code>wp_me_events<\/code><\/li>\n<li><code>wp_me_event_signups<\/code><\/li>\n<li><code>wp_me_event_recurrence<\/code><\/li>\n<li><code>wp_me_event_types<\/code><\/li>\n<li><code>wp_me_resources<\/code><\/li>\n<li><code>wp_me_event_resources<\/code><\/li>\n<li><code>wp_me_event_type_resources<\/code><\/li>\n<li><code>wp_me_workgroups<\/code><\/li>\n<li><code>wp_me_workgroup_members<\/code><\/li>\n<li><code>wp_me_mails<\/code><\/li>\n<li><code>wp_me_newsletters<\/code><\/li>\n<li><code>wp_me_documents<\/code><\/li>\n<li><code>wp_me_document_types<\/code><\/li>\n<li><code>wp_me_rolling_texts<\/code><\/li>\n<li><code>wp_me_calendar_filters<\/code><\/li>\n<li><code>wp_me_finance_journal<\/code><\/li>\n<li><code>wp_me_sync<\/code><\/li>\n<li><code>wp_me_google_calendars<\/code><\/li>\n<li><code>wp_me_google_event_map<\/code><\/li>\n<li><code>wp_me_statistics<\/code><\/li>\n<li><code>wp_me_sponsor_clicks<\/code><\/li>\n<li><code>wp_me_change_history<\/code><\/li>\n<li><code>wp_me_change_undo<\/code><\/li>\n<\/ul><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.6.0<\/h4>\n\n<ul>\n<li>Added: Personal calendar filter. A <strong>Filter<\/strong> button next to \"+ New event\" (both shown only on the Calendar tab) opens filter settings for date range, text (title, resume, description), crew member or open shifts, published on homepage, event types (with Internal\/Public\/Cafe quick picks) and crew roles. Each criterion can show or hide (exclude) its matches. Settings are stored per user in the new <code>me_calendar_filters<\/code> table (not tracked in the change history), the button turns green while a filter is in use, and Clear deletes the settings to return to the default view.<\/li>\n<li>Changed: The calendar list view uses the personal filter's date interval when its date rule is active (instead of the current month), and its title shows that interval (e.g. \"13 Oct \u2013 17 Nov 2026\"). An empty start means all past events and an empty end all future events, shown as \"From \u2026\", \"Until \u2026\" or \"All events\".<\/li>\n<li>Added: Previous (\u00ab) and next (\u00bb) buttons beside the date on the event page jump to the neighbouring event in time order. They follow the personal calendar filter when one is in use (all events otherwise) and keep the open tab.<\/li>\n<li>Changed: Google Calendar sync sends the event's full description (up to 8,000 characters, with paragraph breaks kept) instead of only the first 160 characters on one line. When the description is longer than that, the shorter resume is used instead; without a resume the description is cut at a word boundary. Already-synced events are updated on the next run.<\/li>\n<li>Changed: The Mails section is now called <strong>Marketing<\/strong>, with a megaphone icon on the landing page. It still has the Mails and Newsletters tabs, and the page addresses are unchanged.<\/li>\n<li>Added: Crew shift reminders. A new daily sync job, \"Crew shift reminders\" (disabled by default), e-mails each volunteer assigned to a crew role on tomorrow's shifts. The text is the editable <code>crew_shift_reminder<\/code> system mail with the tags {name}, {event_title}, {shift_start}, {shift_end}, {role_name} and {site_name}. A shift is reminded once, and again only if its member or times change.<\/li>\n<li>Changed: The event types list shows columns for name, scope, cafe opening hours (read-only checkbox), default resources and a colour swatch.<\/li>\n<li>Changed: Every event must have an event type. Creating or saving an event without a valid type is rejected (the editors require a choice instead of offering \"None\"), and an event type still used by events cannot be deleted.<\/li>\n<li>Added: Optional <strong>Get-in time<\/strong> (artist arrival) and <strong>Door opening<\/strong> time on events, edited on the event's general-details tab and stored as <code>get_in_datetime<\/code> \/ <code>door_opening_datetime<\/code>. A time later than the start time is saved on the evening before the start date. Both must be on or before the start, follow the start when it is moved (e.g. by calendar drag), are copied to recurring series events, and are tracked in the change history. The public event page shows the door opening time.<\/li>\n<li>Added: Protected Cafe, Art and Holiday system event types and an editable in_cafe_time flag. Schema upgrades preserve existing type IDs and settings. Cafe-time warning suppression is reserved for a future change.<\/li>\n<li>Fixed: Section headers and browser titles remain Members on Workgroups, Mails on Newsletters, and Finance on Deliveries; the active tab identifies the current subsection.<\/li>\n<li>Fixed: Mails and Newsletters section tabs appear below the page header, matching the other Vis pages.<\/li>\n<li>Improved: Vis navigation groups Members, Calendar, Mails, Finance and Shop into accessible dropdowns with direct links to their page tabs. Newsletters belongs under Mails; Deliveries belongs under Finance; Statistics is a standalone menu item. The landing page uses a two-column tile layout (one column on mobile). Navigation, tabs and the WordPress admin bar share capability-aware section definitions. Existing page URLs remain supported.<\/li>\n<li>Added: <strong>Event publication approval<\/strong> on the Publish tab, visible to all members but editable only with <code>vis_publish_events<\/code>. Homepage approval controls public event tiles, sliders, agenda, newsletter event inclusion, direct public links, embeds and signup\/ticket checkout. Existing events remain approved during upgrade; new events start unpublished. Poster and Facebook flags are stored for future use and do not trigger integrations. Unpublished public links return 404; event embeds show a not-published message. Internal event editors can still manage unpublished events.<\/li>\n<li>Changed: Event detail headers show a compact start\/end date and time, updated after saving. The Delete event button is now on the general-details tab.<\/li>\n<li>Added: Mail and newsletter-template editors now offer inline font sizes and an HTML source mode. Unchanged source HTML is preserved when switching back to the visual editor; saves still use WordPress HTML sanitization.<\/li>\n<li>Fixed: Newsletter emails include upcoming events with full descriptions, dates, locations and main event images between the mail body and shared footer. Email output retains emojis and contains no PDF page breaks.<\/li>\n<li>Added: Shared <strong>Newsletter template<\/strong> system mail under Vis \u2192 Mails. Its header (organization title and media-library logo) and footer are reused by every newsletter email and PDF. The newsletter checkbox reveals read-only previews and an Edit newsletter template button; edit the shared template to update future sends and PDF generations. Existing newsletter-specific footers are retained in stored mail records but are replaced by the shared footer in output. Ordinary mails are unchanged.<\/li>\n<li>Changed: Each event starts on a new page in generated newsletter PDFs. Email content is unchanged.<\/li>\n<li>Fixed: Newsletter PDFs remove unsupported emojis and trailing empty editor lines. Event images are centered above the description and scaled to fit; long descriptions can continue across pages instead of being trapped in a single table row.<\/li>\n<li>Added: <strong>Change history with undo<\/strong> \u2014 all writes to Vis tables are logged in the new <code>me_change_history<\/code> table, with undo data in <code>me_change_undo<\/code>. A new <strong>Vis \u2192 History<\/strong> admin page lists every change with filters and an Undo button, guarded by the new <code>vis_undo_changes<\/code> capability (granted to System Admin).<\/li>\n<li>Added: <strong>History tab<\/strong> on the event page (<code>\/vis\/events\/{id}<\/code>), listing the changes to that event, its sign-ups and resources.<\/li>\n<li>Added: <strong>Member detail page<\/strong> (<code>\/vis\/members\/{id}<\/code>) with a Settings tab (edit fields, Vis role, delete) and a History tab. Rows in the members list now open this page. Extensions can add tabs with the <code>pcio_me_member_tabs<\/code> filter.<\/li>\n<li>Added: developer hooks <code>pcio_me_history_tables<\/code>, <code>pcio_me_history_undo_retention_days<\/code> and <code>pcio_me_history_undone<\/code>.<\/li>\n<li>Added: <strong>Google Calendar sync<\/strong> \u2014 new <strong>Vis \u2192 Google<\/strong> admin page (service-account key, default location, single-day event types, calendars with All\/Public scope, connection test) and a \"Sync Google Calendar\" job under Vis \u2192 Sync Jobs, replacing the legacy <code>\/vis\/sync_calendar.php<\/code> endpoint. Event links are kept per calendar in the new <code>me_google_calendars<\/code> and <code>me_google_event_map<\/code> tables. Changed events are detected from a stored payload hash instead of a shared dirty flag, so failed updates are retried. Uses the WordPress HTTP API, without a Google client library.<\/li>\n<\/ul>\n\n<h4>1.5.0<\/h4>\n\n<ul>\n<li>Fixed: <strong>Roles page membership display<\/strong> \u2014 shows the separately managed <code>vis_member<\/code> capability and explains why the default Member role has no additional capabilities, without changing permission grants.<\/li>\n<\/ul>\n\n<h4>1.5.0<\/h4>\n\n<ul>\n<li>Added: <strong>Rewritten member profile page<\/strong> \u2014 <code>[pcio_me_edit_profile]<\/code> is now a server-rendered form (new <code>templates\/member-profile.php<\/code> plus its own stylesheet) with browser-side validation, an inline error box and a success message. The membership number is shown read-only so members cannot change it. Extensions can add their own fieldsets with the new <code>pcio_me_profile_extra_sections<\/code> filter.<\/li>\n<li>Added: <strong>Member save validation hook<\/strong> \u2014 the new <code>pcio_me_member_update_validate<\/code> filter lets extensions reject a save (from the member dialog or the profile form) before anything is written; the request is answered with a 422 and the extension's message.<\/li>\n<li>Added: <strong>Vis \u2192 Sponsor Links<\/strong> \u2014 a dedicated admin page documenting the <code>pcio-sponsor-link<\/code> class, moved off the Shortcode reference page, with step-by-step instructions for the block editor, the classic editor and Divi (plain text links and Image modules).<\/li>\n<li>Added: <strong>Exact membership-number deep link<\/strong> \u2014 <code>\/vis\/members?member_number=42<\/code> pre-fills the membership-number filter and matches that number exactly, so a short number can no longer match a longer one. Used by the Boat register's crew links.<\/li>\n<li>Added: <strong>\"No members match your search\"<\/strong> message on the <code>[pcio_me_members]<\/code> list when a search filters every row out.<\/li>\n<li>Added: <strong>Signup fulfilment context<\/strong> \u2014 new public methods <code>PCIO_VIS_Member_Signup_Fulfillment::stash_order_context()<\/code>, <code>get_order_context()<\/code>, <code>take_order_provision()<\/code>, <code>claim_order_welcome()<\/code> and <code>is_order_welcome_claimed()<\/code> let extensions running on <code>pcio_mep_order_confirmed<\/code> share the created member, read the provisioned WordPress account, and take over (or hand back) the welcome mail.<\/li>\n<li>Changed: <strong>One membership number instead of two.<\/strong> The separate \"Membership #\" field is retired; the <code>#<\/code> column is now the membership number itself, stored as an integer, and any number of members can share it (that is how a boat or household is grouped).<\/li>\n<li>Changed: <strong>The numbering upgrade migrates your data automatically<\/strong> on the next admin page load. Members with a \"Membership #\" keep that number; members without one get the leading digits of their old \"#\"; a \"#\" that was not a plain number is preserved as a <code>legacy_member_number<\/code> row in <code>wp_me_member_meta<\/code>. The finance journal and the Products extension's subscription table are converted the same way. The routine is safe to re-run and resumes by itself if a step fails.<\/li>\n<li>Upgrade procedure:\n\n<ol>\n<li>Export <code>wp_me_members<\/code> (and <code>wp_me_p_subscriptions<\/code> if you use the Products extension) from your database admin tool, so you can restore the old numbers if anything looks wrong.<\/li>\n<li>Update the plugin, then open any wp-admin page to run the upgrade.<\/li>\n<li>Check <strong>Vis \u2192 Members<\/strong>: several members may now share one number, and a number that used to carry a letter suffix has lost it. Correct anything that looks wrong by hand.<\/li>\n<li>Update the Products, Tickets and Boat extensions to their matching 1.5.0 release. Older extension versions read the retired column, so membership status would look out of date until they are updated.<\/li>\n<li>If the upgrade did not run (for example because a host blocks <code>ALTER TABLE<\/code>), a <code>member_number_legacy_140<\/code> column is left behind on <code>wp_me_members<\/code> holding the original numbers \u2014 leave it in place and open any wp-admin page again; the next attempt resumes from there.<\/li>\n<\/ol><\/li>\n<li>Fixed: the members and finance-journal tables are now actually brought up to date on an existing site. Their <code>CREATE TABLE<\/code> statements no longer use <code>IF NOT EXISTS<\/code>, which made <code>dbDelta()<\/code> treat \"IF\" as the table name and skip the table completely.<\/li>\n<li>Changed: <strong>Collision-free number allocation<\/strong> \u2014 new sign-ups get the next free membership number through the new <code>PCIO_VIS_DB::create_with_next_number()<\/code>, which re-checks for a concurrent claim. Two simultaneous sign-ups can no longer be handed the same number.<\/li>\n<li>Changed: <strong>Member meta writes are hardened<\/strong> \u2014 <code>PCIO_VIS_Meta_DB::set()<\/code> now accepts a member id <em>or<\/em> a member row (or a single-row list) and silently ignores a non-numeric id instead of raising a type error that killed the request.<\/li>\n<li>Changed: <strong>Welcome mail can be rendered without being sent<\/strong> \u2014 <code>render_welcome()<\/code> returns the resolved subject and body (and an empty result when the template, member or address is unusable); <code>send_welcome()<\/code> keeps its old behaviour and now falls back to nothing instead of sending an empty mail.<\/li>\n<li>Changed: <strong>No inline script in the members shortcode<\/strong> \u2014 the live search and the boat detail popup were previously emitted as an inline <code>&lt;script&gt;<\/code> block; the search now lives in <code>assets\/members-public.js<\/code> and the boat popup is gone (boat details are on the profile page).<\/li>\n<li>Changed: <code>PCIO_VIS_DB<\/code> member-number helpers were renamed and aligned: <code>get_next_member_number()<\/code>, <code>get_next_free_member_number()<\/code>, <code>member_number_is_taken()<\/code>, <code>create_with_next_number()<\/code>, <code>get_member_number_by_id()<\/code>, <code>set_member_number()<\/code>, <code>get_member_ids_by_member_number()<\/code> and <code>get_members_by_member_number()<\/code>. The <code>next_member_number()<\/code> \/ <code>next_subscription_number()<\/code> \/ <code>*_subscription_number()<\/code> methods are <strong>removed<\/strong> \u2014 extensions that called them must be updated.<\/li>\n<li>Changed: the finance journal stores the membership number as an integer, and the journal REST payload no longer returns it as a string.<\/li>\n<li>Changed: refreshed the Danish (da_DK) translation and regenerated the translation template (.pot) \u2014 the recurring-events strings, the new Sponsor Links page, the sign-up modes and the rewritten profile form were all untranslated before.<\/li>\n<li>Fixed: member search in the <code>[pcio_me_members]<\/code> list lowercases with <code>mb_strtolower()<\/code>, so Danish letters (\u00d8, \u00c6, \u00c5) now match a search typed with a Danish keyboard.<\/li>\n<li>Fixed: the sponsor-link help section is no longer listed twice (the scroll-spy index and the page no longer disagree).<\/li>\n<\/ul>\n\n<h4>1.4.0<\/h4>\n\n<ul>\n<li>Added: <strong>Recurring events<\/strong> \u2014 a new <strong>Recurrence<\/strong> tab on the event editor. Define a repeat rule on the first event in a series (daily, weekly, monthly by weekday, monthly by date, or yearly) and generate linked copies; the series can be navigated, extended or resolved into standalone events. A new database table stores the series rules.<\/li>\n<li>Added: <strong>Upcoming events shortcode<\/strong> \u2014 <code>[pcio_me_upcoming_events]<\/code> renders a compact, month-grouped agenda list of the next events. Accepts <code>months<\/code> (how far ahead to look) and <code>limit<\/code> (maximum events) attributes.<\/li>\n<li>Added: <strong>Membership number<\/strong> \u2014 a new \"Membership #\" field on member records, sortable in the member list and editable from the member dialog. It lets shop subscriptions reference a specific member.<\/li>\n<li>Added: <strong>Event sign-up modes<\/strong> \u2014 each event can now choose how people respond: <em>No sign-up<\/em> (information only), <em>Simple<\/em> (join \/ interested \/ not joining), or <em>Ticket sales<\/em> when the Tickets extension is active. Choosing <em>No sign-up<\/em> hides the invitation box entirely on the public event page.<\/li>\n<li>Added: <strong>Rich description editor<\/strong> \u2014 the event description editor now supports text and background colour and an <strong>Edit HTML<\/strong> toggle for editing the raw source.<\/li>\n<li>Added: <strong>Week numbers<\/strong> \u2014 the calendar week view title now shows the ISO-8601 week number (e.g. \"Uge 37\").<\/li>\n<li>Changed: <strong>Member deep links<\/strong> \u2014 <code>\/vis\/members?q=\u2026<\/code> pre-filters the member list, so links (for example from shop subscriptions) can jump straight to a member.<\/li>\n<li>Changed: public event pages now inherit the active theme's text colour, so titles and body text stay readable on any page-builder background (e.g. Divi).<\/li>\n<li>Changed: refreshed the Danish (da_DK) translation and updated the translation template (.pot).<\/li>\n<li>Fixed: event creation could fail on sites upgraded from older versions whose events table was missing newer columns; the schema now self-repairs on upgrade.<\/li>\n<li>Fixed: the calendar month title could appear blank or untranslated under Danish; the localised title now renders correctly and is capitalised.<\/li>\n<li>Fixed: the <strong>Edit<\/strong> button on the Calendar \u2192 Event Types and Resources tabs showed the wrong label in non-English languages.<\/li>\n<\/ul>\n\n<h4>1.3.0<\/h4>\n\n<ul>\n<li>Added: <strong>Member sign-up<\/strong> \u2014 new <code>[pcio_me_member_signup]<\/code> shortcode renders a public registration form (name, email, phone, address). It creates the member record, provisions a WordPress account and, with no payment extension active, registers the member immediately. Extensions can inject extra fields and add a payment (checkout) step on submission.<\/li>\n<li>Added: <strong>Welcome email &amp; notifications<\/strong> \u2014 a \"member_welcome\" system mail template is seeded under Vis \u2192 Mails and sent automatically to new members, with tag chips for <code>{name}<\/code>, <code>{member_number}<\/code>, <code>{site_name}<\/code> and a one-time set-password <code>{login_url}<\/code>. Extensions can add their own tags via filters.<\/li>\n<li>Added: <strong>Rolling texts<\/strong> \u2014 new <code>[pcio_me_rolling_text]<\/code> shortcode shows a scrolling banner built from time-scheduled entries. Manage them under Calendar \u2192 Rolling Texts, where each entry has a Roller ID and a start\/stop window; multiple pages can show independent banners.<\/li>\n<li>Added: <strong>Shortcode reference<\/strong> \u2014 new admin page documenting every shortcode with its attributes, defaults, examples and welcome-email tags, including scroll-spy navigation.<\/li>\n<li>Added: <strong>Volunteers mail group<\/strong> and a <strong>Copy emails<\/strong> button on the member and workgroup lists for quick recipient selection.<\/li>\n<li>Fixed: text overlap and layout issues in the admin UI.<\/li>\n<li>Changed: refreshed the Danish (da_DK) translation and updated the translation template (.pot).<\/li>\n<\/ul>\n\n<h4>1.2.0<\/h4>\n\n<ul>\n<li>Added: <strong>Event Types<\/strong> \u2014 color-coded categories for calendar events with configurable access level (Public, Members only, Volunteers only). Managed from the Calendar \u2192 Event Types tab.<\/li>\n<li>Added: <strong>Resources<\/strong> \u2014 bookable resources (rooms, equipment, etc.) that can be assigned to events. Conflict detection warns when a resource is already booked for an overlapping event. Managed from the Calendar \u2192 Resources tab.<\/li>\n<li>Added: <strong>Default resources per event type<\/strong> \u2014 define which resources are pre-selected when a new event of that type is created; defaults are applied automatically on event creation.<\/li>\n<li>Added: <strong>Workgroups<\/strong> \u2014 new page at <code>\/vis\/workgroups\/<\/code> for organizing volunteers into named working groups. Each workgroup can have one or more members flagged as chairman.<\/li>\n<li>Added: <strong>Volunteer role<\/strong> \u2014 members can be designated as volunteers (<code>vis_volunteer<\/code> capability), enabling volunteer-only event access and workgroup membership.<\/li>\n<li>Fixed: \"Publish &amp; share\" heading was double-escaped and displayed as raw HTML entity in the event editor.<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>Added: <strong>Statistics dashboard<\/strong> at <code>\/vis\/statistics\/<\/code> \u2014 grouped bar chart of members joining and leaving by year, front-page-view counter, login counter, and a sponsor link click table.<\/li>\n<li>Added: <code>join_date<\/code> and <code>leave_date<\/code> date fields on member records, editable from the member edit dialog and charted on the Statistics dashboard.<\/li>\n<li>Added: <code>[pcio_me_member_count]<\/code> shortcode \u2014 animated count-up number showing the current total membership. Each page render increments the front-page-view counter. Paste it into any page builder text or code module (e.g. Divi Text Module).<\/li>\n<li>Added: Sponsor link click tracking \u2014 add <code>class=\"pcio-sponsor-link\"<\/code> to any anchor tag in your content. Click events are captured in the browser and stored; totals appear on the Statistics dashboard.<\/li>\n<li>Fixed: end date\/time entered in the calendar create-event dialog was not saved to the event.<\/li>\n<li>Fixed: clicking a single all-day cell now correctly pre-fills the end date with the same date as the start date.<\/li>\n<li>Fixed: end-time field was not hidden when the \"All day\" checkbox was ticked.<\/li>\n<li>Fixed: member list was not refreshed in the browser after a member was successfully deleted.<\/li>\n<li>Fixed: document list shortcode displayed a bullet point before each document link.<\/li>\n<li>Changed: WP Role column removed from the member list; the WordPress account status is now shown only as a \"Create WordPress login\" button in the edit dialog when no account exists yet.<\/li>\n<li>Changed: the edit-member icon in the member list is now shown only to users with the <code>vis_manage_users<\/code> capability; plain members use the new profile shortcode instead.<\/li>\n<li>Added: <code>[pcio_me_edit_profile]<\/code> shortcode \u2014 lets a logged-in member edit their own name, email, phone, address and custom fields from any WordPress page.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release.<\/li>\n<li>Members register with custom member fields and roles.<\/li>\n<li>Event calendar with public sign-ups and per-event banner\/thumbnail images.<\/li>\n<li>Member mailings and public newsletter archive.<\/li>\n<li>Document library grouped by type, backed by the WordPress media library.<\/li>\n<li>Finance journal.<\/li>\n<li>Capability-based access control with cumulative logical roles (Vis \u2192 Roles).<\/li>\n<li>Automatic public pages under <code>\/vis\/<\/code> plus shortcodes for embedding in themes.<\/li>\n<\/ul>","raw_excerpt":"Member and event management for voluntary organizations: members register, event calendar with signups, mailings, newsletters, documents and finance.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/tt.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/338684","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tt.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/tt.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/tt.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=338684"}],"author":[{"embeddable":true,"href":"https:\/\/tt.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/pcio"}],"wp:attachment":[{"href":"https:\/\/tt.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=338684"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/tt.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=338684"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/tt.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=338684"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/tt.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=338684"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/tt.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=338684"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/tt.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=338684"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}