=== Private Space ===
Contributors: smallapis
Tags: members only, restrict content, membership, private page, import users
Requires at least: 6.0
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.3.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Restrict content to members: private pages, members-only parts of a page, accounts from a CSV file. Built on WordPress users. 25 members free.

== Description ==

For a school, a club, an association or a company that needs a handful of pages only its people can
read, and needs to create two hundred accounts from a spreadsheet without doing it by hand.

= How it works =

Tick **Members only** on any page or post, and optionally **its sub-pages too**. From then on:

* A visitor who is not signed in gets a sign-in form **in place of the content**. The page keeps its
  address and its place in menus: everyone can see the members' area exists, only members can read it.
* Someone signed in without access is told so, instead of being shown a sign-in form again.
* The page leaves search results, archives, feeds and the XML sitemap for everyone else, and asks
  search engines not to index it.
* Its comments are hidden and closed for non-members, including in comment feeds.
* The REST API returns the same refusal, for pages, posts and every custom post type.
* Both versions of the page, gated and not, are kept out of shared caches.

= Part of a page only =

Not every page is all or nothing. Wrap what only members should read in a shortcode, and say
something else to everyone else:

`[private_space]The meeting notes and the phone list.[/private_space]`

`[private_space_guests]Members get the notes after each meeting. Ask us to join.[/private_space_guests]`

A visitor sees a short note and a sign-in link where the members' part would be. You can change the
note: `[private_space message="Sign in to see the timetable."]`.

= Your own sign-in page =

Put `[private_space_login]` on any page to make it your sign-in page, choose it in the settings,
and visitors who open a members-only page are sent there, then brought back to the page they wanted
once signed in. Without it, the form simply appears in place of the content.

= Accounts =

Add one person at a time, or **import a CSV**. The import is two steps: the file is read and shown to
you first, with the rows it did not understand, the addresses that already have an account, and how
many fit in the free version. Nothing is created until you confirm.

The import runs in small batches with a progress bar, so two thousand rows never hit the server's time
limit, and the passwords of every batch are kept safe before the next one starts.

The CSV is forgiving on purpose: commas, semicolons or tabs; English or French column headings
(`email`, `courriel`, `first name`, `prénom`, `last name`, `nom`); and the Windows encoding Excel uses
when it saves a French spreadsheet. One column has to be the email address; the rest is optional.

Existing accounts can be given access too, keeping their password and their current role. From the
members list you can search, send someone a link to choose a new password, remove their access
while keeping their account, or **export the whole list as a CSV** that the import reads back as is.

= About passwords, which is the part that matters =

**This plugin does not implement authentication.** It creates ordinary WordPress users and adds one
capability to them. Sign-in, password hashing, resets, sessions and rate limiting are all done by
WordPress itself.

By default WordPress emails each new member a link to choose their own password. No password is ever
put in an email. If you prefer to hand passwords out yourself, they are shown to you once and offered
as a one-time CSV for an hour, stored encrypted with your site's secret keys in the meantime. After
that they are gone: nobody can display them again, including us.

= What this does not protect =

**Files in the media library are not protected.** Anyone with the direct address of a PDF or an image
can open it. That is true of every plugin that does not put a PHP script in front of the uploads
folder. Keep confidential documents out of the media library.

= Free and paid =

25 members are free. Beyond that a Smallapis key lifts the limit, and the same key unlocks every other
plugin in the family on the same site.

= External service =

Only if you enter a licence key, the plugin checks it about once a day with the Smallapis service at
https://smallapis.com/v1/me. The request carries the key and, like every request WordPress
makes, the address of your site. No member data is ever sent. Without a key, nothing is sent anywhere.
Terms and privacy: https://smallapis.com/legal.html

== Installation ==

1. Install and activate. A **Member** role is created.
2. Open **Private Space** and add people, or import your list.
3. Edit any page and tick **Members only** in the sidebar.

== Frequently Asked Questions ==

= Can search engines see a members-only page? =

No. The page asks search engines not to index it, leaves the XML sitemap, search results and feeds,
and its content is never in the HTML sent to someone who is not signed in.

= What about the REST API and RSS feeds? =

Both get the same refusal as the page: the REST API returns a short "reserved for members" text
instead of the content and excerpt, for every post type, and feeds leave the page out.

= What CSV format does the import expect? =

One line per person, with a header line. Only the email column is required; first name and last
name are optional. Commas, semicolons or tabs all work, and the column names can be in English or
French (`email` / `courriel`, `first name` / `prénom`, `last name` / `nom`). A file exported from
Excel, in its Windows encoding, keeps its accents.

= Can I restrict only part of a page? =

Yes, with `[private_space]...[/private_space]`. Everything outside the shortcode stays public. The
page is then kept out of shared caches, so a member's view is never served to someone else.

= Can members see the dashboard? =

The Member role has `read` and one custom capability: no writing, no settings, no access to other
people's details. By default members do not see the WordPress toolbar, and after signing in they land
on your site, on a page you can choose, rather than on their profile.

= What happens to my pages if I deactivate the plugin? =

They become public again immediately, so deactivating is not a safe way to pause a private area. The
role and the accounts stay in place, so reactivating restores everything. Deleting the plugin removes
its settings and the role but never deletes an account.

= Can I use an existing role instead? =

Any user with the `read_private_space` capability passes the gate. Administrators and editors get it
automatically. If you manage roles with another plugin, granting that one capability is enough.

= Does it work with a caching plugin? =

Yes. Private pages send no-cache headers and tell page caches not to store them.

= Does it work on a multisite network? =

Yes, site by site. The role is created on each site the first time its dashboard is opened.

== Screenshots ==

1. The members list, with search and per-person actions.
2. The preview shown before an import creates anything.
3. What a visitor sees on a members-only page.

== Changelog ==

= 1.3.0 =
* New: `[private_space]` restricts part of a page to members, `[private_space_guests]` shows something to everyone else.
* New: `[private_space_login]` and a sign-in page setting: visitors are sent to your own page, then back to where they were going.
* New: export the members list as a CSV that the import reads back as is.
* Changed: pages using the shortcodes are kept out of shared caches, like private pages.
* Changed: clearer directory description, and answers on search engines, the REST API, feeds and the CSV format.

= 1.2.0 =
* New: a Get a key button in the plugin header. After paying, one click activates the key on the site it was bought from, no copy and paste.
* New: once the plugin has done its job, a short request for a review, on its own screens only, which can be put off or turned off.

= 1.1.1 =
* Changed: the Smallapis service moved to its own domain, smallapis.com.

= 1.1.0 =
* New: protect a page's sub-pages with one tick.
* New: give existing accounts access, remove access, send a password link, search and page through
  members.
* New: the import runs in batches with a progress bar and can carry on after an interruption.
* New: choose where members land after signing in; the toolbar is hidden from members.
* New: "Members only" label in the page lists.
* Fixed: a large import could hit the PHP time limit and lose the passwords of accounts it had
  already created.
* Fixed: comments on private pages were visible to everyone, including in comment feeds.
* Fixed: private pages were listed in the XML sitemap.
* Fixed: the REST API only protected pages and posts, not custom post types.
* Fixed: the member's view of a page could be stored by a shared cache; both views now bypass it.
* Fixed: a signed-in visitor without access was shown the sign-in form again.
* Fixed: spreadsheets saved by Excel in Windows encoding lost their accents and French headings.
* Fixed: names starting with = + - or @ could run as formulas when the CSV was opened.
* Fixed: the sign-in form could be reformatted by the theme's paragraph filter.
* Changed: private pages are excluded by id, which is much cheaper than the previous meta query.
* Changed: generated passwords held for download are encrypted.

= 1.0.0 =
* First release.
