Description
MontraFolio gives every user of the site an image portfolio of their own, and lets staff run shared portfolios — events, trips, campaigns — with designated contributors.
- Portfolio, collection, image. A portfolio has one owner and any number of collections; each collection holds images and can be visible or hidden.
- Private by construction. Nothing goes to the Media Library. Files live in a folder the web server does not serve; the plugin checks who may see each image before delivering it.
- Mobile first. Camera and gallery uploads from a mobile device, images resized on the device, reorder by touch, carousel by swipe. Folder drag-and-drop on a desktop feeds the same pipeline.
- A page for the portfolio, a gallery for each collection. The portfolio page presents the cover, the owner and a full description, then its collections as cards. Each collection shows its images as tiles or a carousel: visitors switch between the two, the collection decides which modes are available and which opens first.
- Chronological order. Images can be ordered by their original capture time, across contributors.
- Owners and contributors. One owner administers a portfolio; contributors publish in the collections they were given, or in all of them.
- Limits at four levels. The site sets what a new portfolio and a new collection are born with; from there the portfolio carries its own storage and image ceilings, a collection may narrow them, and each contributor may be narrowed further. No level ever grants more than the level above it, and lowering one deletes nothing — it only narrows the next upload.
- Videos in a collection. Paste the address of a video on YouTube, Vimeo, Dailymotion or TikTok and it takes its place among the images, in the same order, with the same caption and observation. Only its thumbnail is stored here; by default the public page contacts nobody until a visitor presses play.
- Videos on networks that answer nobody. Instagram, Facebook and LinkedIn answer no plugin about an address, so nothing is asked of them: you paste the address, choose the picture the card will show, and the card opens the video in a new tab. Those networks are never contacted by this site — not when you add the video, not when a visitor looks at the collection.
- No telemetry, ever. Sign-in with WordPress accounts; optional single sign-on through any plugin that hooks the WordPress login. The only requests that ever leave the site are the ones videos need, listed under External services.
-
Interface in English, Portuguese and Spanish, independently of the WordPress language.
-
In any page of the theme. Three blocks, each with a shortcode: a collection with its lightbox, the collections of a portfolio, and the descriptions of either.
- Personal data under control. Images are stored without camera metadata; the personal data export of WordPress includes the plugin’s data; a retention rule can remove unused personal portfolios, warning their owners a long time before and letting them keep them with one button; the privacy policy paragraphs are suggested under Settings Privacy.
- Accessible. Keyboard, screen readers, reduced motion and AA contrast on the public pages and in the portfolio area.
Everything described above is in this version and covered by tests: the portfolio area (sign-in, content terms, portfolios, collections, contributors, uploads, limits), the private storage and the image delivery, the public pages and the three blocks with their shortcodes, the wp-admin management screens (the portfolios list with its filters, creation of assigned portfolios, editing, owner change, rename with redirect, contributors, limits, suspension, deletion, storage report, regeneration of the derived sizes), the permissions by role, the personal data exporter and eraser, the retention rule with its notice, and the accessibility and performance passes.
External services
This plugin connects to four video services, and only when somebody uses the video feature. Nothing else on the site ever contacts them, and with every service switched off in Settings Videos no request is made at all.
When an owner or a contributor adds a video by pasting its address, the site asks that service’s public oEmbed endpoint about the address, to learn the title and the address of the thumbnail. What is sent is the address that was pasted and the site’s own address, in the request; the thumbnail is then downloaded once and stored on this site. Nothing about the visitor is involved: this happens while somebody is administering a collection.
When a visitor presses play on a video, that visitor’s browser loads the player from the service, which receives their IP address and whatever their browser sends, and may set cookies. Until play is pressed nothing is requested, unless the site turns Load the video only when the visitor presses play off.
Linked videos contact nobody. The networks the Link a video feature points at — Instagram, Facebook, LinkedIn, and whichever else a site adds to the list — are not external services of this plugin: no request is ever made to them, at any moment, by the site or by the pages it serves. The address is stored as text and the picture is one the site uploaded itself. A visitor who presses the link goes there under their own steam, exactly as they would follow any link written in any post.
The services, with their terms and their privacy policies:
- YouTube (
https://www.youtube.com/oembed, player onyoutube-nocookie.comoryoutube.com) — terms https://www.youtube.com/t/terms · privacy https://policies.google.com/privacy - Vimeo (
https://vimeo.com/api/oembed.json, player onplayer.vimeo.com) — terms https://vimeo.com/terms · privacy https://vimeo.com/privacy - Dailymotion (
https://www.dailymotion.com/services/oembed, player ongeo.dailymotion.com) — terms https://legal.dailymotion.com/en/terms-of-use/ · privacy https://legal.dailymotion.com/en/privacy-policy/ - TikTok (
https://www.tiktok.com/oembed, player ontiktok.com) — terms https://www.tiktok.com/legal/terms-of-service · privacy https://www.tiktok.com/legal/privacy-policy
Source code
Everything this plugin ships is its source. The JavaScript and the CSS under assets/ are written
by hand and served exactly as they are written: there is no build step, no bundler and no minifier,
so what runs in a browser is the file you can open in the plugin’s folder. Nothing here is generated
from something kept elsewhere. The only compiled files are the two third-party libraries below, and
each is named with the address of its own source.
The translations are not carried in the package: WordPress.org delivers them as language packs, and
a site that wants a language nobody has published yet drops the catalog into
wp-content/languages/plugins.
Third-party code
- The QR code component uses qrcode-generator by Kazuhiko Arase (MIT license). The plugin ships the minified build; the unminified source is available at https://github.com/kazuhikoarase/qrcode-generator
- HEIC photos are decoded on the device by libheif (LGPL-3.0), in the WebAssembly build published as libheif-js version 1.23.2. The plugin ships
assets/vendor/libheif/libheif.js(Emscripten glue, minified) andassets/vendor/libheif/libheif.wasm(the compiled library); the sources are at https://github.com/strukturag/libheif and the build that produces these files at https://github.com/catdad-experiments/libheif-js
Screenshots








Blocks
This plugin provides 3 blocks.
- MontraFolio – Collection One collection, as tiles or a carousel, from MontraFolio.
- MontraFolio – Details The description of a portfolio and/or of a collection, from MontraFolio.
- MontraFolio – Portfolio The collections of a portfolio, as cards or as a list, from MontraFolio.
Installation
- Upload the plugin folder to
/wp-content/plugins/or install it from the Plugins screen. - Activate it. The private image folder and the database tables are created on activation.
- Open MontraFolio Settings to choose the address words and the upload limits, and Permissions by Role to decide who manages portfolios.
FAQ
-
Does the plugin send any data anywhere?
-
No usage data, no domain, no versions: the plugin never reports anything about your site to anybody, and updates come from WordPress.org through the normal WordPress mechanism. The single exception is videos, and only where somebody uses them: adding a video asks its service for the title and the thumbnail, and playing one loads it from that service. Both are listed under External services below.
-
Can I put a YouTube video in a collection?
-
Yes — YouTube, Vimeo, Dailymotion or TikTok, by pasting the address of the video. It becomes an item of the collection like an image: same order, same caption, same observation, and it can even be the cover. Nothing of the video is stored on your site except its thumbnail, which counts as one image against the collection’s limits. Each service can be switched off in Settings Videos, and switching them all off removes the feature from the portfolio area.
-
What about a video on Instagram, Facebook or LinkedIn?
-
Those cannot be played inside your pages, and this plugin does not pretend otherwise. Since 2020 Meta answers the Facebook and Instagram oEmbed only to an application registered with Meta, which a plugin installed on sites it knows nothing about cannot be; LinkedIn never published an endpoint at all. So there is no title and no thumbnail to fetch — and nothing is fetched.
Instead there is Link a video: you paste the address and choose a picture of your own for the card. It takes its place in the collection like any other item, and the card opens the address in a new tab. Those networks are never contacted by your site, at any moment, so no script of theirs runs on your pages and no visitor’s address reaches them until that visitor chooses to go. The accepted networks are a list you can edit in Settings Videos.
-
Do my visitors get tracked by the video services?
-
Not until they choose to be. With Load the video only when the visitor presses play on — which is how it is installed — a public page showing a video makes no request at all to the video service: the visitor sees the thumbnail stored on your own server, and a notice saying what pressing play will load. Only then is the player loaded — and it starts playing right away, because pressing play is the gesture browsers ask for before a video may start, so one press is all it takes. YouTube is played from youtube-nocookie.com unless you say otherwise, Vimeo is asked not to keep a session about the viewer, and the text of the notice is yours to write.
-
Do I need another plugin to sign in?
-
No. Users sign in with their WordPress accounts on the plugin’s own page. A single sign-on plugin (Microsoft Entra ID, LDAP and others) is optional and plugs into the WordPress login pipeline.
-
Where are the images stored?
-
In
wp-content/uploads/montrafolio/, a folder protected against direct access on Apache and IIS. On nginx, add thelocationrule described in the manual; the unguessable file names and the delivery through the plugin remain the real defence. -
Do users need wp-admin?
-
No. Owners and contributors work in p-admin, a page of the plugin at the address chosen in Settings (
/p-admin/by default). Users with nothing beyond a reading role land there after signing in and never see wp-admin. -
Can I show a portfolio inside a page of my theme?
-
Yes, with three blocks — MontraFolio – Collection (the gallery), MontraFolio – Details (the descriptions) and MontraFolio – Portfolio (the list of collections) — or their shortcodes,
[mtfl_collection portfolio="ana" collection="trip"],[mtfl_details]and[mtfl_portfolio]. The portfolio is picked on the block itself. The Collection block presents it as tiles, as a carousel or as both, inheriting what the owner proposed when it is not told otherwise; a list of collections can point each line at the same page, so that one page holds the list and the gallery of whichever collection the visitor picks. -
What personal data does the plugin store, and can users export it?
-
The owner name shown on a portfolio and the owner e-mail kept with it, which is never published (copied from the profile, editable by the site staff without changing the account), which account made each contributor designation and each upload, the file name an image had on the device, the content terms accepted (version and date) and the date of the last activity in the portfolio area. Images are stored without camera metadata. Tools Export Personal Data includes all of it. Tools Erase Personal Data deletes the personal portfolio with its images, releases assigned portfolios without touching their content, removes the account’s designations, anonymises the uploader and the device file name of images in other portfolios, and keeps only the acceptance of the content terms, as the proof that what is still published was uploaded under them. Nothing is sent to another server.
-
What happens to a portfolio when its user is deleted?
-
If the person deleting the account attributes its content to another user, that user receives every portfolio of the deleted one. Otherwise Settings Privacy decides for the personal portfolio (delete it with its images, or hand it to a chosen account), and assigned portfolios wait for a new owner. Optionally, a retention period removes the personal portfolios of users with no activity for that long: the portfolio is marked, its owner is written to, the portfolio area offers a button that keeps it, any use of it takes the mark off, and only after the notice period (a year by default) is it deleted.
-
Can I limit how much each person uploads?
-
Yes, at four levels. Settings decides what a new portfolio and a new collection start with. A manager holding set the limits of a portfolio then gives each portfolio its own storage and image ceilings in wp-admin; the owner sees that ceiling and, in the portfolio area, may narrow any collection and any contributor within it. A level set to 0 has no limit of its own and follows the level above. Changing a limit never deletes anything: it decides what the next upload may add, and the meter each person sees is whichever level runs out first for them.
-
Can I put a portfolio inside a page of my theme?
-
Yes — see the blocks above. Beyond that, the pages the plugin serves itself (the sign-in, the content terms, the portfolio area, the public gallery) are documents it writes whole, without the theme’s header and footer: that is what lets a public image be answered before the theme even loads. A site adds what it needs to them through three actions of the plugin —
mtfl_page_head,mtfl_page_body_startandmtfl_page_footer— each given the context of the page, so a consent banner or an analytics script can be placed where the site means it and kept off the sign-in page. The public pages carry their own Open Graph and Twitter tags, built from the title, the description and the cover. -
My host only allows 2 MB uploads. Do I have to change that?
-
No. A photo bigger than one request can carry is sent in parts and put back together on the
server, exactly as a backup file is. The only limit that can make a photo smaller is the one in
Settings Uploads, which is yours to choose. Nothing in the plugin asks you to change a PHP
setting, and it never lowers quality to fit your host’s. -
What about HEIC photos from an iPhone?
-
They are accepted everywhere, and nothing has to be configured for it. Safari converts them itself; every other browser converts them with libheif, which the plugin loads on the device only when such a photo appears. A server whose Imagick was built with HEIC support converts them too, which is only faster; Tools Site Health Info Media Handling says whether yours was.
Reviews
There are no reviews for this plugin.
Contributors & Developers
“MontraFolio” is open source software. The following people have contributed to this plugin.
ContributorsTranslate “MontraFolio” into your language.
Interested in development?
Browse the code, check out the SVN repository, or subscribe to the development log by RSS.
Changelog
1.1.3
- The blocks are found by words in the site’s language. The search words of the three blocks — the ones the editor’s inserter matches — live in
block.json, and the translation catalog did not carry them, so they stayed in English whatever the language of the site. The catalog now holds them, with the title and description written there too, and a language pack translates them along with everything else. - The help under the address parameter reads as a pattern. The two lines that explain it —
?name={portfolio}and?name={collection}— used angle brackets, which translators and their tools took for HTML and asked to keep untouched. Braces say what they mean: put the name of the parameter here and the address there, in any language.
1.1.2
- A changed icon is fetched again. The icon in the wp-admin menu, the one in the browser tab and the one beside the blocks in the editor were served from an address that never changed, so a browser that had already seen an older one went on showing it — sometimes long after the update, with nothing to explain why. They now carry the version of the file, as everything else the plugin serves already did.
- The three blocks now describe themselves on WordPress.org. Each block’s name, title, description and icon move into its own
block.json, the file the directory reads — so the Blocks section of the plugin page tells what MontraFolio – Details, – Portfolio and – Collection each do, instead of listing only their names. Nothing changes in the editor: the same titles, the same descriptions and the same mark as before.
1.1.1
The name is written MontraFolio, with a capital F. It reads that way wherever the plugin names itself: the header WordPress shows, the titles of the three blocks in the editor, the menu of wp-admin, the settings screens, the export and the erasure of personal data, and the line above the title of the pages the plugin serves, for sites that did not put a name of their own there.
- Nothing that addresses anything changed. The folder of the plugin, the shortcodes, the blocks, the REST addresses and the private folder of the images are written exactly as in 1.1.0. A page built with 1.1.0 needs nothing done to it, and an update is only an update of the text.
- The translations were rebuilt with it, so the Portuguese and the Spanish name the plugin the same way.
1.1.0
MontraFolio. The plugin carries its name through everything it names: the blocks, the REST addresses, the folder the images live in and the graphics.
- It has a mark of its own. The monogram appears on the menu in wp-admin, in the tab of every page the plugin serves, and beside its blocks in the editor.
- The blocks and the shortcodes are named for it. MontraFolio – Collection, – Details and – Portfolio, and
[mtfl_collection],[mtfl_details],[mtfl_portfolio]. A page built with an earlier set shows the blocks as unrecognised, and an earlier shortcode prints nothing, until each is written with these names. - What the plugin stores is named for it too, and moves there on the first request after the update: the tables, the settings, the permissions granted by role and what each person’s account remembers. Nothing is copied and nothing is rebuilt — the names change and the rows stay where they are. In the one case where a table cannot move because a table of the new name already holds rows, every screen says so and neither set is touched.
- The private folder is
uploads/montrafolio, and an earlier one is renamed to it on the first request after the update. Nothing is copied and nothing is rebuilt — the folder is renamed with everything in it, and the images go on being served from the same addresses. A site that named its own folder is left alone. In the one case where the rename cannot be done, every screen of the plugin says so plainly, names both folders and says that nothing was deleted. -
The REST addresses are
montrafolio/v1. Only something written against them by hand would notice. -
Translations now come from WordPress.org instead of travelling inside the plugin. They are delivered as language packs and installed into
wp-content/languages/plugins, the folder WordPress keeps for them — which means a translation now survives an update of the plugin instead of being replaced by it, and a site can put a language of its own in that same folder for a language nobody has published yet. Settings Plugin Plugin language, which makes the plugin speak a language the rest of the site does not, reads from there too; a language that is not installed reads in English. Until the packs for a language exist, the plugin reads in English unless its file is placed by hand — the manual says how. - Every form and every address the plugin reads is now taken apart field by field, each value through the sanitizer that field deserves, instead of the request being read whole. Nothing a person does changes: what a form was allowed to save, it still saves, and what it refused, it still refuses. It is the reading itself that is narrower — a field the form does not have never reaches the plugin at all.
- The pages the plugin serves on its own — the sign-in, the portfolio area and the public galleries — now hand their stylesheets and scripts to WordPress instead of writing the tags themselves. Nothing a visitor sees changes: the same files load, in the same order, deferred as before. What it buys is that these pages are no longer invisible to everything that works through WordPress’s own asset list — a caching or optimisation plugin now sees them like any other page, and a site that wants to add a stylesheet of its own, or take one away, has a name to call it by.
1.0.1
- A portfolio now says it changed when its photographs change. The date each portfolio carries only moved when somebody edited a setting of it — a title, a description, a collection created or renamed. An afternoon of uploading moved nothing, so the portfolios that had seen the most work looked exactly like the ones nobody had opened in months. Two screens read that date and both were misleading because of it: the Updated column of the list in wp-admin, and the portfolio picker, which offers the most recently updated first when nothing is typed in it. Photographs arriving and leaving, captions written and images reordered now all move it, and those two screens say what actually happened.
- A collection’s date follows what its images say, and the order they are in. Images arriving and leaving already moved it. Editing a caption, an observation or a capture date moved nothing at all — an afternoon spent captioning a collection left it looking untouched — and neither did reordering by hand or arranging by capture time. All of those are printed on the public page, so all of them are changes now. What it buys today is that a Portfolio block set to Recent orders collections by when they last actually changed; what it buys later is a date worth trusting for anything that needs to know whether a collection is still what it was.
Nothing a visitor sees was changed by this version, and nothing needs doing on update.
1.0.0
First version published on WordPress.org.
What it is: portfolios of images for an organisation, one for each person who wants one and as
many shared ones as the staff need; collections shown as tiles or as a carousel; images kept in
a private folder the plugin serves itself, never in the Media Library; uploads from a phone’s
camera, resized on the device before they travel; limits at four levels; videos hosted elsewhere
added beside the images without a visitor being handed to anybody until they press play; a portfolio
area built for a phone, and management screens in wp-admin for whoever runs the site; the personal
data export and erasure of WordPress, a retention rule for portfolios nobody uses any more, and no
telemetry of any kind.
The development that got here — fifteen versions, from 0.5.0 to 0.9.5 — is in changelog.txt.
