Yumpu Embed – Changelog
Overview · Documentation · License
1.4.12 · October 28, 2026
No code changes.
Changed
- License terms.
LICENSEgains a License Term & Renewal section (each license includes a year of updates and renews yearly; an expired license never disables the add-on), and Support, Updates & Warranty says a current license gives access to the updates released during its term. The same terms as the website's license page and the other Multimediality add-ons. - Changelog format.
CHANGELOG.mdgroups every release under Added, Changed, Removed, Fixed and Security, with Upgrading notes where a release needs a step; the release notes themselves are unchanged.
1.4.11 · October 28, 2026
Changed
- Manual rewritten. The in-app manual (and the same file rendered at multimediality.com/add-ons/yumpu-embed/docs) is reorganised around how the add-on is used: the four control-panel pages one by one, the field from Browse to saving the entry, player settings with the defaults/templates/customizations contract and Advanced Styling, editing and deleting documents with the embed-code panel, every template tag and modifier with sizing, the MCP tool reference with the publish pipeline and pacing guidance, platform and security notes. No code change.
1.4.10 · October 25, 2026
Changed
- Store description: the renewal price is stated ($39 a year, half the license price).
Fixed
- A signed-out session reads as a sentence, not a parser error. Every field action answers JSON, but when the control-panel session has expired or been signed out, ExpressionEngine's secure-form check answers the action's POST with its own HTML error page (HTTP 403) before the add-on runs, and the field reported that page as
Unexpected token '<', "<!doctype "... is not valid JSON(seen when opening Edit Document on a freshly picked document after a session timeout). One shared reader now parses every action response and turns a non-JSON body into the error shape the field already handles: "Your session has expired or you were signed out. Sign in again and retry." for a 401/403 or a sign-in page, and a message naming the HTTP status and the Developer Log for anything else. The two upload paths (server-side File Manager transfer and the browser upload) use the same reader.
1.4.9 · October 11, 2026
Changed
- Store description: the price is set.
Security
- Template management uses EE's own test for the add-on's pages. 1.4.8 gated template create, update, delete and import on the "Can access add-ons" permission alone. ExpressionEngine opens an add-on's control-panel pages only when that permission is set and the add-on is assigned to one of the member's roles (Role → Add-on access), so a member with the permission bit but without the assignment could change templates from the Embed Settings modal while being unable to open the Embed Settings page (found on production right after 1.4.8). The one rule now lives in
Addon::canManageTemplates()— Super Admin, or the permission with Yumpu Embed assigned to a role — and the field's modal, the module endpoints (including the template export) and the two MCP template tools all use it. The docs say so in those words.
1.4.8 · October 11, 2026
Launch-review release: every finding of the 2026-10-10 top-to-bottom review (code, docs, packaging) ahead of the ExpressionEngine add-on store listing. No new features; two permission changes (player-template management and the upload-progress MCP tool) are called out below.
Changed
- The entry cannot be saved while a PDF is still converting. The field is marked invalid with "Upload still in progress — wait for it to finish before saving." and Save is disabled the way a required-field error does it; it clears when the conversion completes, fails or is cancelled. A single failed progress poll no longer fails the conversion (three consecutive failures do), and a Cancel stops a poll still in flight.
- Installing without the PHP curl extension is refused with a clear message, and the Settings page says so if curl goes missing later. The Settings page shows only the last four characters of the saved token, gains Clear saved token, and says when a stored token can no longer be decrypted (the site's encryption key changed) instead of pretending there is none. Embed Settings rejects negative default dimensions.
- Quieter failure modes. The orphan sweep skips objects registered less than 15 minutes ago (a sweep could land between the create and the entry row being written). A failed account-index rebuild is remembered for 90 seconds, so a Yumpu outage costs one timeout per 90 seconds instead of one per render with an expired cover, and an index over 3 MB is not cached. Usage scans preload the Fluid parent links once per field instead of once per hit. The shared first-page Browse request is forgotten after an error response. The settings modal's Escape listener is removed on every close path and a removed Grid row or Fluid block stops reacting to document deletes.
- MCP polish.
openWorldHinton every tool that calls Yumpu (andfalseon the four local ones);pagesno longer lands inget_upload_progress'sfield_value;yumpu_embed_update_documentreturnsunchanged: trueinstead of sending a no-op PUT; a quota-ledger failure never drops a successful result;currentValueaccepts the JSON string or an object; failed Yumpu reads and a half-failed tool registration are written to the Developer Log under the calling tool's name; an oversized template is reported as a normal tool error. - Housekeeping. Remaining hardcoded English in endpoint messages and on the Documents page moved into the language file; 61 unused language keys removed; the no-op
curl_close()calls are gone (deprecated in PHP 8.5). - The manual follows EE's in-app documentation template (
## Usage, a## Disclaimerstating independence from Yumpu), names Test connection, Clear cache, Clear saved token and the template export/import, listslanguageamong the player settings, and states that MCP Server For EE itself needs PHP 8.2 or later and that the CP and the site must share an origin. The website docs gained a License section, one menu path for the add-on's pages, the same requirement notes, and lost a reference to a customer site; the store description gained the client-site exception that the license already contained. This changelog no longer names customer sites or development machines. The license's warranty clause now says support is by email on a best-effort basis instead of promising nothing. - Harness: 293 checks (56 new — token-blanked refusals for the delete and read tools, the template permission gate against a simulated role-less member, the annotation and
minimumschema rules, the once-only post-upload marker, the unchanged-document path).
Fixed
- Every JSON endpoint answers with its real HTTP status as
application/json.json()went throughee()->output->_display(), whose default status argument reset every 400/403/404/502 to 200 and whose Content-Type check reads a header storeset_header()never writes, so EE appendedtext/htmlafter the add-on's JSON header. The helper now sends the headers itself (as theassetsaction already did), withX-Content-Type-Options: nosniffandJSON_HEX_*escaping, so a title or template name can never render as markup when an endpoint is opened directly; an unencodable payload answers 500 with a generic message. The JavaScript always readjson.statefirst, so nothing visible changes. - Live Preview no longer runs the field's save-time work. EE's Live Preview calls each fieldtype's
save()on every preview refresh to normalise the posted value. For this field that was one Yumpu read per refresh and, for an entry with Advanced Styling applied but not yet saved, a new embed object per refresh (the posted value has noembed_idyet), spending the 50-writes-per-15-minutes budget and leaving orphans for the sweep. A preview request now gets the vetted value back and nothing else. - A transient error no longer mints a new embed object. A timeout, a rate-limit answer or a 5xx on an embed PUT was treated like "gone on Yumpu": a new object was created and the old one orphaned — for a document's share object that replaced the hash and killed snippets already handed to clients at the next sweep. Only a real HTTP 404 now falls back to create; everything else is rethrown and logged by the caller with the encoded assumption. The two 404 checks that searched the message text use the typed status.
- "After upload" defaults are applied once per document. Every completed progress poll re-sent the settings Yumpu refuses at creation (one document PUT each), so an MCP client re-polling a finished
progress_idspent the write budget and re-sent the title from the progress response, which could revert a later rename. A sticky per-document marker now skips repeats; the publish page, which stops polling on completion, is unchanged. - A rate limit is reported as a rate limit. Yumpu answers a limited call with HTTP 401 "reached the limit", which the field mapped to "Yumpu rejected the configured API token".
YumpuApiException::isRateLimited()(HTTP 429, or that 401) is tested first and the editor is told to wait; HTTP 429 has its own Verdict line in the Developer Log. - Changing the document keeps the template link. Picking another document copied only
player_settingsonto the new value, so a template-linked entry became a frozen snapshot and template edits stopped reaching it. It now carriesplayer_template_idtoo, and the entry's embed-object keys only when the document is unchanged (a new document gets a fresh object at save). - A fresh install renders the JS player straight away. With no saved Embed Settings a never-customised entry had no settings at all, so the tag took the raw-iframe path (which needs a stored embed URL and never had the 1.2.3
embedded: truefix). Empty settings now mean hub.js defaults; the iframe is kept for id-less legacy values only. - Refusals read as a sentence. The field endpoints answered a refused caller with the raw key
unauthorized_access(it lives in EE's control-panel language file, which front-end actions never load). They now say "You do not have permission to do that." - Fixed an undefined-variable warning (and a wrong "re-read failed" in the Developer Log) in the document-update log line.
- Propagation is site-scoped and counts real writes. Saving one site's Embed Settings or template pushed its styling, with its token, onto every site's defaults-tier entries under Multi-Site Manager;
DocumentUsage::findAll()andfindByTemplateId()now default to the current site (document delete and the usage view still span all sites, since Yumpu ids are account-wide). The 40-call cap counted syncs, and a sync can be a PUT or a POST plus a DELETE; it now counts writes from the quota ledger and stops when fewer than three calls remain in any embed write family. - Template create, update and import survive bad input. A non-text name in an import file raised a TypeError (an EE error page returned to the JavaScript), names over 255 characters hit the column limit under MySQL strict mode, settings were stored unvalidated into a 64 KB column, and two same-named templates in one file both inserted. Non-text and empty names are skipped and reported, names are cut to 255, settings over 60000 bytes are refused with a message, and same-named entries update one template. The export no longer links the source repository.
- Document delete logs embed failures and never overwrites a concurrent edit. Embed-object delete failures were reduced to ids; they are logged with the encoded assumption. Clearing an entry cell now matches the value it just re-read, so an edit between the read and the write is left alone.
- Bad document ids no longer spend a Yumpu read.
yumpu_embed_get_documentandyumpu_embed_build_field_valueaccepteddocumentId <= 0and sent a rate-limitedGET documentthat Yumpu answered with HTTP 400; they refuse first, like their siblings, and every required id in the tool schemas carriesminimum: 1. - Action URLs follow the page's origin. The picker, upload, editor and preview calls fetched absolute site-index URLs with
credentials: 'same-origin'; a control panel on another host or scheme than the site sent them cross-origin without cookies ("Failed to fetch"). Every action URL is now rewritten to the page's origin with its ACT query intact, and the Requirements say the CP and the site must share an origin. - Front-end rendering. The collapse guard skips containers that are not rendered (a hidden tab no longer gets a permanent 70vh) and waits for the player iframe with a MutationObserver (20 s cap) instead of polling; a blocked hub.js falls back to the iframe instead of throwing; player container ids are unique per render; a
width=tag parameter alone no longer forces the height to 100% (each dimension falls through on its own);post_max_size=0reads as unlimited instead of "0 KB"; invalid UTF-8 in a title can neither blank the stored value nor break the player script. - Real unique indexes now back the settings and cache tables: EE's
DB_forge::add_key()ignores the "unique" argument the original migrations passed, so(site_id, key)was a plain index. Migration2026_10_11_000008removes any duplicate rows (keeping the newest) and adds the keys; the cache repository tolerates a lost insert race by falling through to the update.
Security
- An embed object id from the browser is no longer trusted. The entry's hidden value carried
embed_id,embed_document_idandembed_hash, andsave()handed them to the sync untouched, so a forged id let any entry editor PUT onto, or DELETE, any embed object on the Yumpu account — dashboard-made ones and a document's share object included (hashes are public in embed snippets).EmbedSyncServicenow honours a stored id only when the add-on created it (the registry holds it and it is not a share object); anything else is treated as no object, so a fresh one is created and the stray id dropped, and document delete only deletes owned objects. A value whose id was never registered (an entry from before the registry) mints a fresh object on its next save; the old one is left alone. - Stored URLs are vetted on save and on output.
{field:url},{field:thumbnail}, the editor's cover and the iframe fallback printed whateverurl/thumbnail_*/embed_urlthe client had posted, and the save-time enrichment only overwrote them when the embed URL was empty or the thumbnail short-lived. A forged value could reachhreforsrcunescaped. Every stored URL must now behttps://with no quotes, markup, whitespace or control characters; anything else is blanked (which makes the save re-read the document) and the modifiers apply the same rule at output. Signed S3 URLs with&pass unchanged. - The stored value is reduced to the keys the field writes.
save()keepsid,title,url,thumbnail_small,thumbnail_medium,embed_code,embed_url,file_id,file_name,player_settings,player_template_id,embed_id,embed_document_id,embed_hashandpending_upload, casts the ids to integers and requiresplayer_settingsto be a map; acurrentValuewith unknown keys handed toyumpu_embed_build_field_valueis therefore stripped at the entry save. - Error text can no longer inject markup into the field. The field's status line wrote
innerHTML, and most callers passedjson.message,e.messageor the post-upload error, which relay Yumpu's own error text. It now writes plain text (the document editor already escaped). - The Edit Document modal's "used in" list respects channel access.
document_get_settingsreturned the title and CP link of every entry holding the document, whatever channel it sat in. Members who are not Super Admins now see only entries in channels assigned to them on the current site; the exact total and a hidden count are still returned, and the modal says how many placements it could not list. - Failures that are not Yumpu API errors no longer reach the browser verbatim. The
documentsandupload_progressactions relayed any exception message (a PDO error can name tables and paths). OnlyYumpuApiExceptionmessages pass through; everything else is written to the Developer Log and the editor sees a generic message. - Link PDF checks directory access.
EeFile::forLink()and the linked-file-name refresh checked the site and the extension but not the member's access to the upload directory, so a file from a restricted directory could be linked by id and its name and public URL learned; both now require access, as the upload path always did. A PDF is recognised by its%PDF-header rather than by any binary labelledapplication/octet-stream, and a host without the fileinfo extension no longer fatals. - Template management is an administrator action. Creating, updating, deleting and importing player templates now requires the "Can access add-ons" permission at the field endpoints, in the Embed Settings modal (which hides the template name box and Save as Template for other members) and in the
yumpu_embed_create_player_template/yumpu_embed_update_player_templateMCP tools. Listing and applying templates stays open to every entry editor. A template update propagates to up to 40 embed objects, which is why it is no longer an editor-level action. - The module implements EE's
Strict_XID, so the CSRF check can never be skipped for a request flagged as AJAX (the field's own requests were already strict). Views and route files carry theBASEPATHguard, and every folder in the add-on has anindex.html. yumpu_embed_get_upload_progressis a write-scope tool. Its first completed poll applies the "after upload" defaults with one document PUT, but it was registered under read scope withreadOnlyHint: true, so a read-only token could trigger a write that the host does not audit. It now requiresmcp:write(a client that polls has just uploaded, so it holds that scope) and is annotatedreadOnlyHint: false, idempotentHint: true, openWorldHint: true. The tool name is unchanged, so it stays enabled on the host; its description says what it applies.
1.4.7 · October 4, 2026
Fixed
- Titles with
&or quotes no longer gain a level of HTML encoding on every write. Yumpu's single-document read (document.json) returnstitleanddescriptionHTML-encoded (&,"), while the listing returns them raw and a PUT stores whatever it is sent. Every path that read a document and wrote its title back — the post-upload defaults applied when a conversion finishes, the Edit Document modal,yumpu_embed_update_document— therefore stored the literal&on Yumpu, one level deeper each time (seen on a live site: a title with an&became&amp;on Yumpu right after an upload), and the field stored the encoded title, so the publish page showed&and the Edit Document form offered it for saving.YumpuApinow decodes those two fields once on every single-document response (read, update, conversion progress); stored values written by earlier versions are read as raw text and rewritten that way on the entry's next save;{field:title}escapes on output, so templates print exactly what they printed before. Theurlslug the single read derives from the encoded text (…-amp-…) is replaced by the clean one from the same response's embed-code link, which is what the listing returns — so the publish-page refresh no longer sees title and URL differ between its two sources for such a document. A document already double-encoded on Yumpu needs its title re-saved once (Edit Document, or the dashboard). Recorded as a Live-API fact.
1.4.6 · September 26, 2026
Fixes from the 1.4.5 review. No new features.
Changed
- Manual corrections. The Changelog section pointed at
CHANGELOG.md"next to this file", but it ships at the zip root, not insideyumpu_embed/; the sentence now says so. Its fourteen per-release###headings each became a side-navigation entry in the Add-on Manager (EE builds that nav from every h2 and h3); releases are now one bullet each. The Field settings paragraph states the toggle-driven precedence above. Both renderer facts (h2/h3 are nav; only indented or language-less fenced code becomes a copy box) are recorded for future releases. - Tool description without a host minor.
yumpu_embed_quota's usage text named "MCP Server For EE 0.24.0+"; the one public floor is 1.0.0, so the clause is gone (the fact stays in this file's history). - One button-row class. The Save/Test/Clear row, the Backup & Restore rows and the four Embed Settings tool rows shared a hand-copied inline
display:flex;gap:8pxstyle; they now use.ye-btn-row(plus--spacedfor the 12px margins) from the shared settings styles. - Harness follows the host's container flag. MCP Server For EE 0.25.0 marks a Grid or Fluid field
after_write: trueinget_channelwhen one of its columns or children is a Yumpu Embed field (thefield_reportskey is the container's name), so the step-31 check that no other field carried the flag failed against a 0.25.1 host with a Yumpu Grid column and Fluid child. The check now expects the flag on exactly those containers and still refuses it on any field without a Yumpu placement. Not shipped;scripts/test_mcp_tools.phponly.
Fixed
- Field settings are saved from what EE hands the fieldtype.
save_settings()read the raw$_POSTinstead of its$dataargument, so a field created or updated without an HTTP form (a programmaticChannelField::set(), an import, a CLI or MCP-host field tool) silently storeduse_global_defaults=yand blank overrides, and a Grid column of this type could inherit whatever the hidden channel-level Field Options inputs held on the same form (the New Field form renders every installed fieldtype's options; an existing field's form renders only the types compatible with its own, so the leak needed a new Grid field whose type had first been set to Yumpu Embed and its boxes filled). It now reads$data(the posted settings for a channel field,col_settingsfor a Grid column), as the native fieldtypes do; Grid columns keep no settings and use the site defaults, as documented. - The sizing toggle decides.
resolveDimensions()applied a filled override width or height even with "Use global defaults" set to Yes, while the settings form's text (and the manual) said Yes renders the Embed Settings defaults. The override branch now runs only when the toggle is No; Yes ignores the override boxes. The instruction text says so in as many words, and the override descriptions say "used only when the toggle above is No". Tag parameters still override both. - Build script checks the shipped files first. The missing-file
fail()ran after the previous same-version zip had been deleted and the output stream opened, so a missingLICENSEor manual left a 0-bytedist/yumpu_embed_<version>.zipunder the release name. The check now runs before the zip is touched, and the zip-root list is one uniform{name, from}shape.
1.4.5 · September 17, 2026
Store-readiness release, from the pre-listing review. No behaviour change on the publish page or the front end.
Changed
- Minimum versions declared.
addon.setup.phpnow carriesrequires(php8.1,ee7.2.0), which the Add-on Manager enforces at install with its own message. The 7.2.0 floor is real: the route-based control panel (ControlPanel/Routes, the sidebar) arrived in EE 7.2, and every doc had said "7.x". The README, the store copy and the website docs now say 7.2 or later. - Manual inside the Control Panel. EE renders a
README.mdfound in the add-on folder as the add-on's Manual page in the Add-on Manager (level-2 and level-3 headings become the side navigation, code blocks become copyable boxes).yumpu_embed/README.mdis now that manual — requirements, install/upgrade/uninstall, the four settings pages, the field, every tag and modifier, the MCP tools, the Yumpu platform notes, a one-line-per-release changelog and the license summary — and the build copies it to the zip root. The repo-root README is developer-facing (layout, build, where the agent docs live) and is no longer shipped: customers receive the zip, not the source, so its pointers to the development notes were dead ends for them, and its "still planned" list named features that shipped in 1.0.0. - Field settings in EE7's native form.
display_settings()returned nothing and pushed rows through the legacyee()->tableAPI, which EE 7 still honours but renders in the EE2-era two-column style. It now returns the shared-form structure every native fieldtype uses (a "Field Options" group shown only while Yumpu Embed is the selected type): the same three settings (use_global_defaultsas a yes/no toggle,override_width,override_heightas text) with instruction text, the samey/nand free-text values, sosave_settings()and the dimension resolution are untouched and existing field settings read back unchanged. Grid columns never went through this form and still use the site defaults. - Store copy and docs corrected.
docs/description.mdsaid eighteen MCP tools (nineteen since 1.3.0) and "MCP Server For EE 0.10.0+"; it, the manual and the website docs now state MCP Server For EE 1.0.0 or later as the one floor (the host's 1.0.0 carries everything the tools, the field-type hint and the save-time quota report need), and the description gained the pricing and licence paragraph the launch punchlist asked for (price to be filled in). - Build script fails loudly when a file the zip must carry is missing, instead of warning and shipping without it.
- Settings page buttons breathe. Save, Test connection and Clear cache sat flush against each other; they now use EE7's
button button--primary/button--secondaryclasses in the same 8px-gap row as the Backup & Restore buttons, and the lone Save buttons on Upload Defaults and Embed Settings use the same classes (the EE2-erabtnclasses are gone from the views).
1.4.4 · September 12, 2026
Changed
- The publish-page snapshot refresh reads the account index, not one document per row. On every publish-page load the field JavaScript compares the stored snapshot (title, URL, embed code, thumbnails) with the live document and rewrites the value when something real changed. Until now that was one
GET documentper Yumpu row per page open — the last per-document fan-out left in the add-on, and on a Grid or Fluid field full of Yumpu rows the same arithmetic that rules out per-document reads on the front end. The refresh now asks a newmode=snapshotof the field's documents endpoint, answered from the hourly account index (fromIndex(), the same source{field:thumbnail}and the 1.4.3 first-paint cover use): twoGET documentscalls an hour for the whole account, rebuilt lazily on demand, under a different quota family from the reads a bulk loop spends. Trade-offs, all bounded: a rename shows on the publish page up to an hour later instead of fifteen minutes; a document the index does not hold (uploaded on yumpu.com in the last five minutes, deleted, or beyond the 2000-document cap) leaves the stored snapshot as it is, which is what a failed fetch always did; the index's two-hour thumbnail URLs are only written over an expired or changed one, and the next save re-reads any short-lived thumbnail through the single-document read that signs for a year, so a rendered document still stores a year-long URL. The manual Load button keeps the fresh single-document read. The parity check written for this found one difference between the two sources: the single-document read appends a<br><a>title</a>link after the iframe inembed_code, the listing returns the bare iframe (now a recorded Live-API fact). Compared verbatim, that would have rewritten the value on every load and the save would have written the linked form back, so the refresh now comparesembed_codethrough the iframe URL only, copying the liveembed_codealongside a changed URL. Harness: the index and the single read are proved to agree on title, URL, embed URL and thumbnail paths for a live document (the keys the refresh compares), the embed-code difference is pinned so a change on Yumpu's side shows, and the endpoint branch and the JavaScript's compared keys are checked in source. Verified in the browser: the publish page's refresh callsmode=snapshotand gets a 200 with the seven expected keys, and the cover paints from the stored year-long signature. - Empty Yumpu placements share one first-page Browse load. With auto-load on, every empty Yumpu field, Grid row and Fluid block on a publish page fetched the same first Browse page for itself — seven identical requests on one entry in the browser test above. Only the first could reach Yumpu (the browse read is cached fifteen minutes), so this was wasted HTTP rather than quota, but it is gone: the first placement to ask fetches and the rest await the same in-page promise, each rendering its own copy of the rows. Only that automatic initial load is shared; Browse, Change, paging and the reload after an upload always fetch, so nothing stale survives a mutation.
1.4.3 · September 12, 2026
Changed
{field:thumbnail}docs state the page-cache caveat. The modifier always serves a URL valid at render time (the stored signature while it lasts, else the account index, rebuilt on demand at most hourly), but a cached page keeps whatever URL it was rendered with, and an un-rendered document's URLs live two hours — so pages using the tag belong outside any template, static or CDN cache longer than an hour, and a cover that must survive any cache belongs in a file field of its own (which is what the site that raised it does). Two follow-ups noted for later: the publish-page JavaScript refresh could read the account index instead of the per-document read if editor quota ever matters, and a durable Yumpu cover would be one copied into the File Manager at selection time.- Harness guards the fieldtype under test. EE's add-on model includes the deployed
ft.yumpu_embed.phpwhile the host registrar validates the field-type hint, with no class guard, so the harness's fieldtype checks always run against the deployed copy. The harness now fails when that file differs from the working tree, so a linted fieldtype is synced before a run rather than tested stale. - Pacing text leads with the common case. The 1.4.2 wording treated the un-rendered document as the exception. On an account with pre-2026 dashboard uploads it is the rule (three quarters of the account that raised it), and those older entries are exactly the ones bulk maintenance re-saves, so the
build_field_valuedescription, the quota tool'susagetext and the docs now say "assume every save re-reads its document" and name the rendered document as the only case that skips it.
Fixed
- Publish-page cover rendered live server-side, closing a flash rather than a hole. A review of the 1.4.2 quota numbers raised the worry that the stored thumbnail URLs of un-rendered documents (two-hour signatures whatever
image_expiryasks; most pre-2026 dashboard uploads — three quarters of the account that raised it) would leave broken covers on those pages and publish forms. Neither was true: the front-end{field:thumbnail}modifier has served a live account-index URL for an expired signature since 0.9.0, that site's templates draw every cover from a file field anyway, and the field's JavaScript has refreshed the publish-page snapshot on every load since 0.9.0 (it fetches the live document, rewrites the value with fresh thumbnails and re-renders the selected state). What remained was the moment between the server render and that fetch, when the selected state showed the expired stored URL, and the case where the fetch fails. The field now renders the stored URL while its signature is valid and otherwise the same live account-index URL the modifier uses (one cached account listing per hour, no per-entry document read), so the cover is right from the first paint. Display only: the stored value is untouched. Also worth knowing for pacing: the JavaScript refresh is oneGET documentper document per fifteen minutes (the same cached readbuild_field_valueuses), so editors opening publish pages spend the same quota family as a bulk loop, and the ledger counts it.
1.4.2 · September 12, 2026
Changed
- The save-side re-read clause, isolated from the cache and stated precisely. The 1.4.1 live test with an AI client could only observe cache hits, and noticed that every thumbnail signature it saw expired within two hours — so the thirty-day clause looked like it might never prune a read. It does, but only for HTML-rendered documents: Yumpu honours the year-long
image_expiryfor those and ignores it for un-rendered ones (two-hourimg.yumpu.comURLs whatever is asked; most pre-2026 dashboard uploads, which is what the tested entry held). So for a rendered document the clause skips the read on every save until the stored value is about eleven months old; for an un-rendered document every save re-reads, and the fifteen-minute cache alone delivers the one-read-per-document cost. The harness now isolates the clause the way the tester suggested: the real fieldtypesave()on a synthetic stored value with an id that is never cached — a year-long signature leaves the ledger untouched, a two-hour one adds exactly one (uncached, swallowed 404)GET document. Thebuild_field_valuedescription, the quota tool'susagetext and the docs say which documents fall on which side. - Correction to the 1.4.1 note. "Half what the original pacing guidance assumed" was loose: with builds and saves served by one cache, a bulk-link pass over distinct documents still pays one read per document; the saving applies to repeat touches of the same document. What the original 165-entry run over-paid was its two fifteen-minute pauses, which
yumpu_embed_quotanow makes unnecessary.
1.4.1 · September 12, 2026
Changed
- The save-side cost model, measured and stated. The 1.4.0 live test with an AI client read the numbers the new
field_reportsblock exposes: a write of a stored value opened a fresh window at oneGET document(the save re-read the document), and a batch write of the same value 37 seconds later left the count at one (the save reused the cache). The rule in the fieldtype: a save re-reads the document only when the stored value lacks its embed URL or its thumbnail URL expires within 30 days (an upload result with two-hour URLs, a value about eleven months old, a value from before 0.9.0), and that read goes through the same fifteen-minute cacheyumpu_embed_build_field_valuefills. So a build-then-save pair on one document costs one call, not two, and bulk linking costs oneGET documentper distinct document per fifteen minutes — half what the original pacing guidance assumed. Thebuild_field_valuedescription, the quota tool'susagetext and the docs now say exactly that. afterWrite()'s "never throws" claim is now tested. No tool-surface input can make the callback fail (a bad value is refused before the save), so the harness proves the empty-array path on a ledger built over a cache whose reads throw:familyBlock()throws on it,record()swallows it, andafterWrite()returns an empty array. The callback gained an optional second parameter for that seam only; the host still calls it with one argument.
1.4.0 · September 12, 2026
Added
- Entry saves report the Yumpu quota (MCP Server For EE 0.24.0+). The field-type hint the add-on registers with the host now carries
after_write, the host's new provider callback: after everycreate_entry,update_entryorupdate_entriessave whose call touched a Yumpu Embed field (a top-level field, a Grid column or a Fluid child), the host callsMcpTools::afterWrite()and attaches its return to the entry result asfield_reports.<field_name>.quota— the sameGET documentblockyumpu_embed_quotaandyumpu_embed_build_field_valuereport (calls used, budget, remaining, window reset, next slot, last rate limit), read from the local ledger with no Yumpu call. This closes the last gap in pacing on observed usage: a save of a value fromyumpu_embed_get_upload_progressspends oneGET document(two-hour thumbnails make the fieldtype re-read the document once), and the client now sees that call in the save's own result instead of in the next Yumpu tool call. Never on a dry run, never after a failed save, and the callback never throws (an empty report on any failure). Hosts before 0.24.0 keep only the hint keys they know, so the registration is harmless there and nothing changes on the wire. Harness: 10 new checks, including the host's client checklist step 31 driven through the host's own EntryTools and ChannelTools against 0.24.0 (get_channelmarks the fieldafter_write: true,update_entrypassing the current value back carries the report and itscalls_usedmatchesyumpu_embed_quotaright after, a dry run and a title-only update carry none,update_entriescarries it per item only).
1.3.1 · September 12, 2026
Changed
- The dropped-link failure mode is now said at the point of use.
yumpu_embed_build_field_valuereplaces the whole field value, so a build withoutcurrentValue(and withoutfileId) on an entry that already links a PDF silently drops that link together with the player settings and embed object — the save succeeds and{field:pdf_url}just stops resolving (a failure seen on a live site, reproduced from the other direction in the 1.3.0 live test with an AI client). The tool description, the field-type hint the host'sget_channelshows, and the website docs now state it, and the tool'susagetext warns whenever neithercurrentValuenorfileIdwas given. Harness: the warning appears without a current value and is absent with one. - Pacing text corrected: an entry save after
build_field_valuecosts no Yumpu call. The 1.3.0 text told clients to "keep a margin for the entry save". The fieldtype enriches a value on save only when its thumbnails are short-lived, through the same fifteen-minute cache the build fills; a build's value carries year-long URLs, so its save reads nothing. A value fromget_upload_progress(two-hour URLs) costs oneGET documenton its first save, and the ledger counts that call like any other, so the next quota read shows it. The tool descriptions, the quota tool'susagetext and the docs say so; the margin to keep is for other clients of the same Yumpu token, which the ledger cannot see. Harness: the read a save performs after a build is checked to be a cache hit that leaves the ledger unchanged.
1.3.0 · September 11, 2026
Added
yumpu_embed_quota(new, read): how much of the Yumpu rate limit this site has used. Yumpu limits each method family separately (150 per 15 minutes for reads such asGET document,GET documents,GET document/progress; 50 forPOST document/file,POST document/url,PUT document,DELETE documentand the embed-object writes — the add-on'sApiQuota::BUDGETScopies the Limits page), but the add-on only ever offered guidance in a tool description, so an assistant linking 165 entries on 2026-09-11 paused fifteen minutes twice without coming close to the limit. Every callYumpuApisends now lands in a per-site rolling ledger (a sticky row in the add-on's cache table, per method family, 15-minute window; cache hits are never counted, a failed call is, and Yumpu's "reached the limit" answer stamps the family). The new tool reports, per family,calls_used,calls_budget,calls_remaining,window_resets_at(when every counted call has left the window),next_slot_at(when one more call fits once the budget is spent) andlast_rate_limited_at, pluswindow_secondsandnow; it costs no Yumpu call.yumpu_embed_build_field_valueandyumpu_embed_get_upload_progressreturn the same block asquotafor the family they consume (GET document,GET document/progress), so a bulk loop or a poll paces on numbers. The count is a floor: another site or any other client of the same token (the Yumpu dashboard) spends the budget invisibly, and the tool says so. Read scope,readOnlyHint; enable it under MCP Server For EE → Tools → External tools (nineteen tools now). Harness: 17 new checks (ledger arithmetic under a throwaway key, rate-limit detection, the live uncached read counted once, the block onbuild_field_value).
1.2.3 · September 10, 2026
Fixed
- Documents without an HTML5 render now get the iframe player (logo, menu, no print redirect). For a document Yumpu has never HTML-rendered — every pre-2026 dashboard upload; three quarters of the account that raised it on 2026-09-10 — hub.js's
create_player()did not build the/embed/js/iframe at all: it asked Yumpu for the document's render state and, finding no HTML5 version, drew its legacy in-page WebGL canvas player instead. That player has a stripped toolbar (page count, slider, zoom, search, fullscreen), no logo or menu whatever the Logo toggle said, and is the path behind the top-level print redirect noted earlier. The same documents render correctly through a dashboard embed hash because that is always an iframe. The add-on now passes hub.js's ownembedded: trueon every player call — the front-end player, the Embed Settings live preview, and the Embed Code JS snippet — which makes hub.js build the iframe straight away, the identical URL it builds for HTML-rendered documents (verified for Ironworkers Local No. 8), and Yumpu's current player shows image-only documents with the logo. The flag is set by the add-on, not offered as a setting, and set last so no stored legacy value can turn it off.
1.2.2 · September 9, 2026
Fixed
- Linked PDF name follows a File Manager rename. The
file_namestored besidefile_idin a field value (the linked PDF, or a pending File Manager upload) is a label only —{field:pdf_url}and{field:file_id}resolve by id — but a rename in the File Manager, or through MCP Server For EE's newupdate_filetool (0.18.0), left the "PDF in File Manager: …" line and the upload panel showing the old name until the link was redone. The fieldtype now refreshes that name from the File model whenever the value is displayed or saved, so the publish form is right immediately and the stored value catches up on the entry's next save; a file that no longer exists on the site leaves the value untouched, and the lookup can never break a save or a render. The same refresh (EeFile::refreshLinkedFileName()) runs on thecurrentValuethatyumpu_embed_build_field_valueechoes back, and the field-type hint the add-on registers with the host now says thatfile_idis the authoritative link andfile_namea label that may lag until the next save, so an assistant looks files up by id.scripts/test_mcp_tools.phpchecks the stale-name case.
1.2.1 · September 6, 2026
Changed
- Field CSS/JS served as cached files. The shared field stylesheet and script (about 120 KB) were inlined into every publish page, Embed Settings, and Documents page load. They are now delivered by a new public
assetsaction ast=css/t=jsfiles with a year-long immutable cache and an ETag keyed by a content hash (thevin the URL), so a browser fetches them once per add-on change instead of on every page. The stylesheet still goes in the head (no unstyled flash) and the script in the footer. Until the add-on update registers the action, the field inlines the blob as before, so nothing breaks on an install that has not run the update yet. The files contain the same static code every page inlined before — nothing site- or member-specific — which is why the action is unauthenticated.
1.2.0 · September 6, 2026
Added
- The field describes its value to MCP Server For EE. The extension now also subscribes to the host's
mcp_server_for_ee_register_field_typeshook (MCP Server For EE 0.14.0+) and registers the Yumpu Embed fieldtype's value format, so the host'sget_channelreports the field withbuild_with: yumpu_embed_build_field_valueand avalue_formatthat tells an assistant to obtain the value from the tools (build_field_value, or thefield_valuea completed upload returns), pass an existing value ascurrentValue, and link the PDF withfileId— instead of the bare "string" it showed before, which invited hand-written values that lost player settings and orphaned embed objects. The hint appears only once MCP Server For EE 0.14.0 or later is installed; on older hosts nothing changes. Existing installs gain the second extension row on the add-on update (the extension ensures both hook rows on install and update).
1.1.0 · September 6, 2026
MCP parity with the field, so an agent can run the whole publish pipeline (PDF into the File Manager, to Yumpu, linked into the entry) and bulk-link existing documents to existing PDFs. After installing, enable the new tool under MCP Server For EE → Tools → External tools (every added tool starts disabled).
Added
yumpu_embed_upload_document_from_file(new, write). Sends a PDF that is already in the File Manager to Yumpu server-side by file ID, the MCP twin of the field's Choose Existing upload: same file checks (this site, a directory the member can access, stored locally, a PDF by extension and content), same site Upload Defaults with per-upload overrides, same multipart POST. Until now the only MCP upload was from a public URL, which cannot work on a non-public development site and never carried the file link. Returns the progress ID and the file reference.yumpu_embed_get_upload_progressreturns a readyfield_value. On completion the response carries the field value built from the document already in hand (no second, rate-limited document read), with optionalfileId(links the PDF) andplayerTemplateId(applies a template). Both are validated before Yumpu is called.yumpu_embed_build_field_value: Link PDF and merge. OptionalfileIdwrites the File Manager PDF'sfile_idandfile_nameinto the value, the same two keys the field's Link PDF control writes, so{field:pdf_url}and{field:file_id}resolve for entries linked through an assistant; the file must exist on the site and be a PDF. OptionalcurrentValue(the field's stored JSON as the host'sget_entryreturns it) makes the result a merge: the document is replaced and everything else is kept — player settings, template link, the Advanced Styling embed object keys (the fieldtype re-binds the object at save), and an existing PDF link when nofileIdis given; explicit settings replace the kept ones and a pending upload is dropped. Without it, updating an entry that already held a value silently lost its settings and orphaned its embed object. The response reportslinked_file,kept_from_current, and pacing guidance for bulk loops (about 60 entries per quarter hour against Yumpu's per-method read limit).
Changed
- The PDF upload directory is enforced, and advertised. The field's Choose Existing and Link PDF pickers are scoped to the upload directory set in Upload Defaults; the MCP twins now hold the same line:
upload_document_from_file,build_field_valuewithfileId, andget_upload_progresswithfileIdrefuse a PDF from any other directory, naming both directories in the error, and their descriptions state the required directory and its id at registration time so an assistant uploads the PDF there first with the host'supload_file. With no directory configured (Yumpu-only uploads) any File Manager PDF is accepted. The field's own upload endpoint applies the same check server-side (the picker already prevented it in the UI). EeFileis the one File Manager resolver (the module's upload endpoint and the MCP tools share it), including the directory check.- Harness: 34 new checks (link, merge, refusals, directory contract, progress and upload input validation).
1.0.3 · September 6, 2026
Fixed
- Orphan sweep summary adds up. The sentence from
yumpu_embed_find_orphaned_embeds/yumpu_embed_delete_orphaned_embeds(and the Embed Settings sweep) introduced its breakdown with the player-embed count while the "not made by this add-on" figure was counted over every object on the account, so the parts did not sum to the headline (e.g. "247 player embed(s) … 1 in use … 436 not made by this add-on"). It now leads with the account total and shows the player count as an aside: "437 embed object(s) on the account (247 player embed(s)): 1 in use by entries, 0 open preview(s), 436 not made by this add-on (left alone)". The structuredinventorywas already consistent.
1.0.2 · September 6, 2026
Fixed
- No more unstyled flash on publish pages. The fieldtype's stylesheet was appended to the page footer together with its script, so a Yumpu Embed field painted once without its CSS — cover image and action buttons unconstrained for a frame — before the styles arrived.
FieldAssetsnow exposescss()andjs()(a lossless split of the existing blob), and the fieldtype, the Documents page, and the Embed Settings page send the CSS to the document head and only the script to the footer.
1.0.1 · September 5, 2026
Added
- MCP tool annotations. Every tool now declares MCP tool annotations through the host's new
annotationskey (MCP Server For EE 0.12.0+; older hosts ignore it): the nine read toolsreadOnlyHint, the write toolsdestructiveHint: falsewithidempotentHintset honestly,yumpu_embed_delete_documentdestructiveHint: true. Clients can now run reads without prompting and confirm destructive calls.
Changed
- Orphan sweep split into two tools, per the host's guidance for dry-run/delete combinations:
yumpu_embed_find_orphaned_embeds(read scope,readOnlyHint) returns the inventory and the orphans;yumpu_embed_delete_orphaned_embeds(admin scope,destructiveHint, Super Admins only) deletes them after re-running the inventory.yumpu_embed_sweep_embed_objectsis retired — enable the two new tools on the host's External tools page (seventeen tools now).
1.0.0 · September 5, 2026
First public release. Everything below 1.0.0 was pre-launch development; the 1.0.0 zip is the 0.13.1 add-on plus the items below.
Added
- Embed Code in Edit Document, styled like the site. The modal now ends with the same copy-and-paste snippets as the Yumpu dashboard's Embed Code panel — Responsive iFrame (full width, fixed height, page dimensions stamped on the wrapper), iFrame, and a JavaScript player snippet — each with a Copy button, so admins can hand a client the code without visiting yumpu.com. The dashboard's snippets embed a full player object carrying its template's look; the hash the API returns with a document is only a bare preset that renders with Yumpu's defaults (dark background). Style with this entry's player settings therefore creates one durable "share" embed object per document (
EmbedShareService) mirroring the entry's effective player settings — background and fullscreen colours (bare hex ortransparent, both verified to render 2026-09-05), page view, start page, autoflip, the player-element toggles, and the Advanced Styling keys — and rebuilds the snippets on it; Refresh styling updates the same object in place so snippets already handed out pick up the new look. Share objects are registered, kept by the orphan sweep, never touched by entry saves, and deleted only with the document. Opening the modal from an entry styles the document automatically the first time, when it has no share object yet, so the snippets are ready to copy on first open; it never restyles automatically afterwards (an outside site may embed that object), so changing the look stays an explicit Refresh styling. On the Documents CP page, where there is no entry, a chooser styles from the site default player settings or any saved player template. Unstyled snippets are still offered there until styled, labelled as such. New front-end actionembed_share(registered on add-on update); the read-only MCP toolyumpu_embed_get_embed_codereturns the styled snippets when a share object exists, and the write-scopeyumpu_embed_style_embed_code(document id, optional player template id) creates or refreshes the share object from the site defaults or that template and returns them — sixteen tools now; enable the new ones on the host's External tools page. Player Permissions always come from the document.
Changed
- Requirements stated explicitly: ExpressionEngine 7.x on PHP 8.1 or later with the
curlextension (the add-on lints clean on PHP 8.0–8.3; 8.1 is the supported floor). - LICENSE year updated to 2026.
Fixed
- Required fields need a real document. The fieldtype now implements
validate(): a required Yumpu Embed field (plain, Grid, or Fluid) is rejected with EE's standard "This field is required." unless it holds a Yumpu document id. Previously EE only tested that the raw value was non-empty, so a pending upload (a PDF chosen from the File Manager but never sent to Yumpu) satisfied a required field and the entry could publish with no flipbook. Pending values still save on optional fields so the editor can come back and finish the upload.
0.13.1 · September 5, 2026
Changed
docs_urlset in the add-on setup (https://multimediality.com/add-ons/yumpu-embed/docs); README documents uninstall behaviour, the pending-upload conditional, and Grid/Fluid use.
Security
yumpu_embed_delete_documentnow declares theadminMCP scope (it waswrite), so a write-only token can never reach an irreversible account-wide delete; the in-handler Super Admin check stays. Enable the tool again if your client's token lacksmcp:admin.
0.13.0 · September 5, 2026
Added
- Link PDF. A document that never went through the field's upload panel (picked from Browse, loaded by ID, or uploaded on the Yumpu dashboard) had no File Manager PDF behind it, so
{field:pdf_url}and{field:file_id}were empty for it and the only way to attach one was to re-upload. When an EE upload directory is configured in Upload Defaults, the selected state now shows the linked PDF (or "No PDF from the File Manager is linked to this document") with Link PDF / Change PDF (EE's file picker on that directory, existing files only) and Unlink. Linking writes the file's id and name into the field value on the next save; nothing is uploaded and Yumpu is not called. Non-PDF picks are refused inline. Works in Grid and Fluid placements.
0.12.0 · September 5, 2026
Added
- Orphaned embed-object sweep. The add-on now records every embed object it creates (
EmbedRegistry, a sticky entry in its cache table — no migration), because Yumpu's API cannot tell an orphaned add-on object from one made on the dashboard. A collapsed "Embed Objects" section on the Embed Settings page offers Check embed objects (an inventory: player objects on the account, in use by entries, open previews, not made by the add-on) and, for Super Admins, Delete orphaned embed objects, which removes only registered objects that no entry value references and no open editor is previewing (up to 40 per run), and forgets registry entries whose object is already gone. Also the MCP toolyumpu_embed_sweep_embed_objects(dry run by default). Objects created before this version are unregistered and are left alone.
Changed
- Advanced Styling edits propagate immediately. Saving a player template (modal, Embed Settings page, or MCP) now brings the Yumpu embed objects of every entry live-linked to it up to date on the spot, and saving the site defaults does the same for entries on the defaults tier (no own settings, no template) — previously those objects only caught up on each entry's next save.
EmbedPropagationServicedrives each placement through the same sync the entry save uses (nothing is called when no advanced key is involved or the fingerprint already matches), writes the new embed state back into the stored value after re-checking the cell, and stops at 40 API calls per save to stay under Yumpu's 50-per-15-minute embed write limit — anything beyond is reported and catches up on its next save. The template response, the Embed Settings alert, and the Developer Log carry the summary.
Fixed
- Required-field validation now follows the field's value. EE's publish form validates live by listening for change events on a field's inputs; the Yumpu field wrote its hidden value from script with no event, so after a failed save on a required field EE kept the field flagged and Save disabled even once a document was selected, and clearing the field never showed "This field is required" until submit. Every write now fires the event: clearing a required field flags it at once, selecting a document clears the flag and re-enables Save. The reverse problem is fixed too: after a failed save the server-rendered "This field is required" state vanished a moment later. EE's drag-and-drop file widget (the "PDF in File Manager" upload inside the field) clears the invalid class on its nearest fieldset and removes the error message under its nearest field control every time it re-renders, which wiped the Yumpu field's own error. The widget now sits in a faux fieldset and field control of its own, so its housekeeping stays inside the widget; its change events also stop at the field's container instead of triggering a validation round-trip.
- Fixed a scratch preview embed leaking when the editor was applied or closed while its first create request was still in flight (the release ran before the object had an id and skipped); the release is now deferred until that request completes. A scratch id counts as an open preview for 3 hours instead of a day, so a preview whose release never arrives becomes sweepable the same day.
0.11.0 · September 4, 2026
Added
- Document delete (Super Admins only): a "Delete on Yumpu…" button in the Edit Document modal (field and Documents page) opens a confirmation that states the blast radius from the usage scan — the places that will be cleared — and requires typing the document ID. On confirm the document is deleted on Yumpu first (nothing in EE changes if Yumpu refuses), then every placement holding it is cleared (plain fields, Fluid blocks, Grid cells, Grids inside Fluid — each cell is re-checked to still hold that document before it is blanked), the embed styling objects those placements owned are deleted (they survive the document on Yumpu's side and would render 404), and the add-on and EE caches are cleared. The deletion is refused when the usage scan fails, since the affected entries would be unknown. The deletion is announced to every Yumpu field on the page — Grid rows, Fluid blocks, other fields — so each instance that held the document clears itself (the server already cleared the stored values; an instance left showing the old value would have written the deleted id back on the next entry save), Browse cards drop it, and an instance that had not browsed yet loads the list instead of showing "No results". Also available as the MCP tool
yumpu_embed_delete_document(write, Super Admin,confirmDocumentIdmust repeat the id). New ACT actiondocument_delete— run the add-on update. Probed live: DELETE answers 200, the document is gone from GET and the listing at once, a repeat DELETE is 404. - The field can now be used in Grid columns and Fluid fields. The fieldtype never declared those content types to EE (the base class answers "channel" only), so it was missing from the Grid column and Fluid field pickers even though its markup, JS initializer, save, and render paths were written for cells and blocks. It now accepts
channel,grid, andfluid_field. - Entry usage scanner:
DocumentUsagefinds every entry that holds a given Yumpu document (or embed object) in a Yumpu Embed field — plain channel fields, Grid columns, Fluid blocks, and Grids inside Fluid — with the entry title, where in the entry it sits, and the CP edit link. Surfaced as a "Used in N entries" box at the top of the Edit Document modal (field and Documents page) and as the read-only MCP toolyumpu_embed_document_usage. It is the blast-radius check document delete will require. All four locations are harness-verified against a dev-site entry holding one document in a Grid column, a Fluid block, and a Grid inside a Fluid block (EE 7.5 stores a Fluid block's value in the child field's table withentry_id = 0— only the Fluid link row names the entry, which the scanner now follows).
Fixed
- Picker cards inside a full-width Grid or Fluid block no longer push their Select/Edit buttons outside the card: six columns now need ~380px per card (2340px of container) instead of 1860px, and the button row wraps when it must.
- A pending upload survives the entry save. An editor who drops a PDF into the field (or picks one with Choose Existing) in EE-directory mode but does not click Upload to Yumpu no longer comes back to an empty field: the fieldtype stores a small
pending_uploadstate (the EE file id, its name, and the typed title — nothing else) and the field reopens the upload panel from it — EE's file widget shows the chosen PDF as a saved file field would, a line names it as "Waiting to be sent to Yumpu", the title is prefilled, and Upload to Yumpu is ready. Cancel, selecting a document, or completing the upload clears it. Nothing is enriched or synced for a pending value and the front end renders nothing for it; modifiers return empty. Only File Manager picks can be remembered — a file chosen from the browser's disk still has to be re-chosen. Note for templates:{if field}is true for a pending value (EE tests the raw string); use{if field:id}to test for a real document.
0.10.0 · September 3, 2026
Added
- Advanced Styling (Phase 4): a new collapsed "Advanced Styling" section in the Embed Settings editor (field modal and the Embed Settings page) holds the settings only a server-stored Yumpu embed object can carry: header title, show title / username / header menu, title font family, size, color, bold and italic, auto-hide menu bar, table of contents, and page overview. Progressive enhancement, no mode to pick: the keys live inside the same player settings map as everything else (templates, site defaults, live-linking, and the MCP
settingsargument all work unchanged), and any non-default value makes the entry render through its own Yumpu embed object — created, updated in place, re-bound when the document changes, or deleted (when every advanced value is cleared) by the fieldtype when the entry is saved (EmbedSyncService), then passed to hub.js asembed_id. The basic settings keep coming from the JS call, which overrides the object's copies. A template or site-default styling edit reaches an entry on that entry's next save; API failures never block the save (logged under Developer → Logs and retried next time). - Live preview stays live: the editor previews advanced styling through a scratch embed object per open editor (
embed_previewaction, updated on each change;embed_preview_releasedeletes it when the editor closes or the page is left). The scratch object is tracked per member so the release endpoint cannot delete anything else; an entry's own embed object is never touched by the preview, so cancelling the editor changes nothing live. - Applied After Upload (Upload Defaults page): the five settings Yumpu's upload API refuses — show more magazines, show information, printing on Webkiosk, the recommended-documents sidebar, HTML5 fullscreen on yumpu.com — are now saved to each new document by the add-on the moment its conversion finishes (field upload and MCP upload alike), so nobody has to visit the Yumpu dashboard after uploading. Probed live: a new document's public player is built the first time its embed page is opened in a browser, so a PUT made right after conversion is what the player serves. A refusal never fails the upload (warning in the field, structured Developer Log entry, Edit Document as the fallback); if the upload page is closed before conversion finishes, the defaults are not applied. The keys travel with the Upload Defaults backup/restore; stored under
UploadParams::POST_UPLOAD_KEYS, never sent on the create request. - Developer Log entries for failed Yumpu calls now say what to fix. Every API failure is a
YumpuApiExceptioncarrying the method and endpoint, the HTTP status, Yumpu's per-field messages, and the parameter keys sent (never values).DeveloperLogger::logApiFailure()writes one structured entry per failure: what the add-on was doing, the request, the keys, what Yumpu rejected, whether it looks like a contract change (a field rejection rather than auth, rate limit, or not-found), the site context, the encoded assumption the call relies on (UploadParams::ASSUMPTION,DocumentParams::ASSUMPTION,EmbedParams::ASSUMPTION— each a one-line summary of the live-API facts that class encodes, with the constant to change), and the fix path (re-probe, update the class, the recorded facts, CHANGELOG). Applied to uploads (field, file, and MCP URL), progress polling, document settings read/save (modal and MCP), preview embeds, and the entry-save embed sync. Harness: 5 pure checks. - Two new ACT actions (
embed_preview,embed_preview_release) — run the add-on update on existing installs; until then the editor previews without server-side styling. New librariesEmbedParams(single source of truth for the advanced keys, defaults, wire form) andEmbedSyncService; newYumpuApiembed methods;CacheRepository::clearAll()now leavessticky:keys alone. Field values gainembed_id,embed_document_id, andembed_hash. Harness: 7 pureEmbedParamschecks.
Changed
- Live-probed against the embed API (POST/PUT/GET/DELETE on
embed.jsonplus the rendered/embed/js/<hash>player page, 2026-09-03): PUT needsdocument_idandtypealongsideidand replaces the whole settings object (unsent keys reset to defaults), writes are all-or-nothing, values are y/n only (the numeric/on-off read-back forms are rejected), colors are bare 6-digit hex (a leading#is rejected),titleis write-only but rendered, and the fiveplayer_c2r_*click-to-read keys are "Not allowed" on a player embed — click-to-read does not return through this path. Only the keys above demonstrably reach the player: toolbar, icon and arrow colors, the outline, the text-font flags, "powered by", and the flip type are accepted and stored by the API but the player page keeps serving Yumpu's defaults for them, so they are not offered (EmbedParams::DORMANT_KEYSkeeps them one promotion away). Cause: the renderer only honors#-prefixed stored colors, which the dashboard writes and the API refuses in every form — a Yumpu-side mismatch, not plan gating. Also verified: the dashboard mints a new embed object on every save, whereas the add-on updates its one object per entry in place. - The document-level "Player" group is now Player Permissions (Edit Document modal and Upload Defaults page), and notes clear up the two "player" layers: the Embed Settings modal's Fullscreen Elements section says those toggles affect the fullscreen view only (the embedded preview does not change when toggled) and that the buttons exist at all only when the document's permissions allow them; the Player Permissions group says the reverse (they apply everywhere the document is embedded; with a permission on, the embedded view always shows the button and the fullscreen toggles decide fullscreen). Verified against the player source: in embedded mode the download/print/share buttons are gated by the document's
player_download_pdf/player_print_page/player_social_sharing, while hub.js'sdownload/print/shareparameters only govern fullscreen. Also found: the player reads those permissions from a memcached document JSON that did not pick up an API PUT for over two hours, so a permission saved through Edit Document can lag in the player (noted in the modal). - Uploads now default to Yumpu's
htmlrendering type (UploadParams::DEFAULT_RENDERING_TYPE; the Upload Defaults dropdown lists it first as recommended). Found the hard way: Yumpu's own default,instant, converts the page images only and leaves the HTML5 player version for "later" — the public side answers "Document deleted" for such a document, a browser visit only builds a temporary cached copy that hands the document to the Hornet WebGL player (stripped toolbar, console errors, and on the front end a redirect to a print URL), and one instant upload was still unrendered 28 hours later. Withhtmlthe conversion returnsis_html_rendering: trueand the document is embeddable the moment it finishes (verified 2026-09-04 with an html upload and an instant control upload made minutes apart).rendering_typecannot be changed after upload. The upload-progress responses (field and MCP) now carryhtml_rendered; the field's completion message says "ready to embed" when true and explains the wait, and the setting to change, when false. Related: Yumpu honors the 1-yearimage_expiryonly for HTML-rendered documents (unrendered ones serve 2-hourimg.yumpu.comURLs regardless), another reasonhtmlis the default. - Found a per-day upload cap on this plan (HTTP 401 "This API key has reached the limit for this method" on the sixth file upload of the day, far below the documented 50 per 15 minutes); it surfaces through the existing token-rejected message.
- Document PUT now sends the documented
site_social_sharing/site_recommended_magazinesnames instead of the aliases the docs mark deprecated (PUT accepts both, verified). Uploads keep the aliases: a 2026-09-03 re-probe ofdocument/file.jsonconfirmed the create endpoints still reject the preferred names with "Not allowed".
Fixed
- The upload/conversion progress bar is now EE7's own
.progress-barcomponent (themed track and striped accent fill, the same bar the CP file uploader shows) instead of a hand-styled one — and it actually appears: since 0.7.0 the bar was shown by clearing its inline display, which fell back to the stylesheet's hidden state, so only the "Converting… 33%" text line was ever visible. - Fixed two
Undefined variable $idEscwarnings (shown to logged-in admins above the front end) from the JS player renderer whenever an embed rendered without an explicit height: the collapse-guard script was built before the escaped container id was assigned. The guard itself never worked in that state either — its element lookup used an empty id. Now that it runs, it measures the player iframe rather than the add-on's container: a padding-bottom aspect-ratio wrapper (the common responsive-embed CSS, which positions the iframe absolutely) leaves the container at zero height while the player is sized correctly, and the container-only check would force a 70vh height into it — on a wrapper with an inlineheight:100%that inflated the embed to ~1500px. The guard now waits for hub.js to create the iframe (up to ~4 s) and rescues only when the iframe itself has no height.
0.9.1 · September 3, 2026
Added
- Upload Defaults backup & restore: a collapsed "Backup & Restore" section on the Upload Defaults page exports the saved defaults as a JSON file and imports one back (for example on a fresh install). The file is a versioned envelope (
format,format_version,addon_version, the key list, the settings, and the EE upload directory id); import validates everything before saving anything — malformed JSON, a foreign format, a file from a newer add-on version, unknown settings keys, or invalid values (bad toggle/enum/category values, non-text values, oversized files) are rejected with a message naming the problem, and nothing is written. The EE upload directory is restored only if a directory with that id exists on the site (otherwise "Yumpu only", with a notice). BumpingUploadDefaultsTransfer::FORMAT_VERSIONis the breaking-change lever for future settings changes. Harness covers the round-trip and every rejection.
Changed
- Player template export filename now carries the date (
yumpu_embed_player_templates_YYYY-MM-DD.json), like the upload-defaults export.
Fixed
- Developer Log entries were never written (since 0.7.0): the
exp_developer_log.hashcolumn has no default, so under MySQL's default strict mode every insert failed and the logger's error guard hid it. Each entry now carries a unique hash, and a failing logger falls back to PHP's error log instead of vanishing. Every upload, document edit, settings save, and MCP write logged by the add-on now actually appears under Developer → Logs.
0.9.0 · September 2, 2026
Added
- Document editing (Phase 3): change a Yumpu document's title and settings without leaving ExpressionEngine. Edit on every Browse card (next to Select) and Edit Document on the field's selected state open an Edit Document modal — Basic Information, Visibility, Player, Analytics, and PROKiosk groups mirroring the Upload Defaults page, prefilled from the document's live settings — that saves straight to Yumpu (
PUT /document.json), clears the browse cache, and refreshes the field's stored title/thumbnail. Entry-editor gated like the rest of the field endpoints. Live-probed against document PUT: the five settings the create endpoints refuse (show more magazines, show information, printing on Webkiosk, the player's recommended-documents sidebar, HTML5 fullscreen) ARE editable here, so a document can be configured completely after upload; only the zoom/quality sliders stay yumpu.com-only. - Documents CP page: new sidebar page that hosts the same picker in manage mode (Browse + ID/URL lookup, no Select/Upload) so administrators can edit any document in the account from the add-on settings.
- MCP:
yumpu_embed_get_document_settings(read) andyumpu_embed_update_document(write; read-merge-write — only the keys you pass change) join the tools. Now eleven tools.
Changed
- Card grid snaps to 1/2/3/4/6 columns (divisors of 12) by the container's own width instead of auto-filling, so a page never ends in a short last row (the Documents page shows 4 columns with the CP sidebar expanded and 6 with it collapsed). "Documents per page" on the Settings page is now a dropdown of multiples of 12 (12–96) for the same reason; previously saved free-form values snap to the nearest option.
- Live-API finding: document PUT rejects empty values (
Field "description": Value empty,Field "tags": Value is empty) even when the current value is blank, so blank fields are omitted from the request (unsent keys keep their current values) and a description/tags/GA code can be changed but not cleared through the API. - New
DocumentParams(single source of truth for post-upload editing: PUT whitelist, settings-blob normalization, wire names, plan-gate pruning);YumpuApi::getDocumentFull()/updateDocument(); ACT actionsdocument_get_settingsanddocument_update(registered on add-on update). The picker CSS moved from the fieldtype into the shared asset blob so the CP page can reuse it; the category/language option normalizer moved from the Upload route intoYumpuDocumentService.
Fixed
- Vanishing covers fixed: Yumpu's thumbnail URLs are signed and expired after 2 hours (the API default), so covers stored in entries went blank. Single-document reads now request the 1-year maximum (
image_expiry; the list endpoint refuses the parameter, so the fieldtype re-reads a freshly selected or uploaded document through the single read at entry-save time), the publish page refreshes a selected document's stored title/cover/embed data from Yumpu on load (persisted on the next entry save — this also picks up renames made on the Documents page or from another entry), and{field:thumbnail}resolves a live URL from a cached account-wide index when the stored one has expired, so existing entries display again without being re-saved.
0.8.0 · September 1, 2026
Added
- EE File Manager integration: a new "EE upload directory" setting on the Upload Defaults page makes the field a dual-destination upload — every PDF uploaded through the field is first registered in that ExpressionEngine upload directory (through EE's own Filemanager: name cleaning, duplicate handling, MIME safety check, File Manager listing) and Yumpu then receives it from its stored location. The EE
file_id/file_nameare kept in the field value alongside the Yumpu document. Leave the setting blank for Yumpu-only uploads (previous behavior). - Native EE drag-and-drop widget: with an EE upload directory configured, the upload panel is ExpressionEngine's own file-field widget pointed at that directory — the familiar "Drop File(s) Here to Upload / Upload to Directory", Choose Existing, and Upload New, with EE handling naming conflicts, subfolders, and directory permissions. Whichever way a PDF arrives (drop, Upload New, or Choose Existing), it lands in the File Manager first and the title is prefilled; Upload to Yumpu then sends it server-side through the new
upload_from_fileaction — the browser posts only the file id, and the server re-checks site, directory access, on-disk presence, and PDF type before reading it. - Drop zone without an EE directory: when no directory is configured the panel is a lightweight drop zone ("Drop a PDF here, or click to choose one") that also opens automatically when a file is dragged over the picker, uploading straight to Yumpu as before.
- Template modifiers so the separate Book PDF field can be retired:
{field:title},{field:thumbnail}(size="small"optional),{field:url},{field:id},{field:file_id}, and{field:pdf_url}(the EE file's public URL when the PDF was stored in EE; per-request memoized). - The selected state shows which File Manager PDF backs the document. New ACT action
upload_from_file(registered on add-on update).
Changed
- Player templates are now live-linked: entries that applied a template without further changes render the template's CURRENT settings, so editing a template updates every embed still linked to it (hand-customizing an entry detaches it and keeps its snapshot; a deleted template falls back to the snapshot). Completes the three-tier model — site defaults propagate to untouched entries, templates propagate to linked entries, customizations are frozen. Descriptions in the CP updated to say so.
- Picker pagination: the documents endpoint's now-reliable
totalpowers "Showing X–Y of N" and a Last button (jumps to the final page). Next/has_more still derives from the limit+1 fetch so paging survives a stale total, and a Last jump landing past the real end steps back a page instead of stranding on an empty grid.
0.7.0 · August 31, 2026
Added
- PDF upload: upload PDFs to the Yumpu account directly from the entry publish page — an Upload button in the field picker opens a panel with file + title (prefilled from the filename), shows real upload progress, then Yumpu's conversion progress, and auto-selects the finished document into the field. Entry-editor gated, like the rest of the field endpoints. Oversized files are rejected client-side against the server's real limits (min of PHP's
upload_max_filesize/post_max_sizeand Yumpu's 600 MB). - Upload Defaults page: new sidebar page mirroring Yumpu's own post-upload settings screens (Basic Information, Visibility, Player, Analytics, collapsed PROKiosk & Advanced). Saved site-wide and applied to every upload; Google Play Product ID and zoom/rendering sliders have no API params and are documented as yumpu.com-only.
- MCP upload tools:
yumpu_embed_upload_document_from_url(public-URL transport; site defaults + per-upload overrides),yumpu_embed_get_upload_progress, andyumpu_embed_list_categoriesjoin the existing six tools. - New ACT actions
uploadandupload_progress(registered on add-on update); migration addsdefault_upload_settings;YumpuApigains multipart/POST support with per-call timeouts and surfaces Yumpu error messages from non-200 responses; category/language lists cached ~1 day; the browse cache clears when a conversion completes so the new document appears in Browse immediately. - API failures are logged to the EE Developer Log with request shape (param keys, file size, title length — never values), and Yumpu error bodies are surfaced verbatim in field errors.
Changed
- MCP player-settings whitelist synced to the current working option set (legacy c2r/shadow/embedded keys no longer accepted).
- Front-end sizing contract: the JS player no longer inherits the embed code's baked 512×384 — explicit chain (tag → field → Embed Settings defaults) or
100%×100%governed by the template wrapper, with a runtime collapse guard (70vh fallback + console explanation) when the wrapper is unsized; the raw-iframe fallback still borrows embed-code dimensions as its last resort.
Fixed
- Live-API corrections from end-to-end testing (the docs lag the live API; all encoded in
UploadParamsand comments): uploads return HTTP 202 (all 2xx now accepted); the create endpoints reject the docs'site_*param names — legacy aliasessocial_sharing/recommended_magazinesare used on the wire, and printing/show-more/show-information are dashboard-only (removed from the form, noted in the UI); unsent params fall back to the Yumpu ACCOUNT's presets, so the Upload Defaults page is WYSIWYG (sends everything configured, pruning only plan-gatediapat 'n');visibilitygained the undocumenteddlink("Direct link") value andprivateis honestly labeled "Only me".
0.6.0 · August 24, 2026
Changed
- Default documents per page is now 12 — a lighter first load for new installs that still divides evenly into the card grid's 2/3/4-column layouts (25 left a lone card on the last row); the per-site setting still overrides it
- Picker responsiveness now keys off the field's own width (container query at 520px): the URL/ID input and Load/Browse buttons wrap to full-width rows, and cards reflow to title/ID on top, centered cover, full-width Select below
- Pagination polish: page loads show pulsing skeleton cards holding the grid's shape (no more clear-and-jump on Next/Prev; the pager stays visible, disabled while loading), and a First button jumps back to page one (no "last page" button is possible — the API does not report a reliable total)
- Document cards redesigned: horizontal layout with the cover at full card height on the left and title, ID, and a bottom-anchored Select button on the right; the whole card is clickable with a hover accent, uses the sharper medium thumbnail, and the grid auto-fills responsively (replaces the fixed 5-column breakpoints)
- Document picker rework: removed the API search input and browse/search dropdown (Yumpu's Search API queries all public Yumpu content, not the account, so it never belonged in an account picker) along with the inaccurate "up to 150" text. The picker is now one row — document URL/ID input, Load, Browse. Load resolves the ID through a new
mode=getendpoint and selects the document with its real title and cover (previously it stored a bare ID with no preview); unknown IDs show an error instead of selecting blind.
Fixed
- The field's Change button now loads a fresh first page of documents instead of showing a blank picker
- Clearer no-connection behavior in the field: a missing token shows "not configured" guidance and a token Yumpu rejects (401/403) now reads "Yumpu rejected the configured API token…" instead of a raw HTTP error
0.5.0 · August 24, 2026
Added
- Site-wide default player settings: new embeds render with them on the front-end and the entry Embed Settings modal opens seeded with them; entries keep their own snapshot once applied
- Player templates can be edited in the live preview editor on the Embed Settings page (Edit loads the template into the editor; Update saves it back), alongside the existing import/export/delete
Changed
- CP settings rebuilt as route-based pages with an EE7 sidebar (matching the mcp-server-for-ee house structure): Settings (API token, auto-load toggle — now a native EE toggle instead of a dropdown — and a new "documents per page" setting for the picker) and Embed Settings (default embed dimensions, site-wide default player settings with an inline live-preview editor, and player template management)
- Front-end embeds follow Yumpu's JS FAQ for multiple magazines per page: all embeds share a single
Yumpuobject (was one per embed — Yumpu's documented anti-pattern that re-loads player resources per instance) and defer creation until DOM readiness - Player settings form rebuilt around the options hub.js actually forwards to the player (verified against hub.js source: for documents with an HTML5 version it renders an iframe embed passing exactly search/autoflip/startpage/pageview/pagenumber/logo/fsbgcolor/bgcolor/share/download/print). Removed dead legacy options (Click-to-Read, shadows, embedded mode, fallback order, page overview, recommended, fullscreen overlay, disable mobile FS — silently ignored by the modern player); added the two working options that were missing: Autoflip and Search. Old stored settings remain harmless.
- Player settings form: binary yes/no and on/off dropdowns replaced with native EE toggle switches (stored values unchanged); Basic section starts collapsed
- Settings editor layout: container-query responsive stacking (preview wraps below the form based on the editor's own width, not the viewport), 450px settings column and 600px height on the settings page, theme-aware scrollbar
- Shared settings-form/preview JS extracted to
libraries/FieldAssets.php, used by both the publish-page fieldtype and the settings pages - Templates export moved to an ACT action; migration adds
default_per_pageanddefault_player_settingscolumns (run the add-on update)
0.4.0 · August 24, 2026
Added
- MCP integration: the add-on now registers six MCP tools (
yumpu_embed_list_documents,yumpu_embed_get_document,yumpu_embed_list_player_templates,yumpu_embed_create_player_template,yumpu_embed_update_player_template,yumpu_embed_build_field_value) with the MCP Server For EE add-on (0.10.0+) via itsmcp_server_for_ee_register_toolshook, letting AI assistants browse Yumpu documents, manage player templates, and build ready-to-store field values for the Yumpu Embed fieldtype. Tools are opt-in per tool in the MCP add-on's control panel. No behavior change when MCP Server For EE is not installed.
0.3.0 · August 23, 2026
Changed
- Embed Settings modal: the template used for an embed is remembered (
player_template_id) and preselected when reopening; hand-editing settings clears the selection when values no longer match, and Apply only stores the template id when settings exactly match it - Manual selection accepts document view/read URLs, account edit URLs, and numeric IDs; unresolvable input now shows an error instead of being silently dropped on save
- Theme pass: all buttons now use EE7's native
button button--primary/--secondaryclasses (the old EE2-erabtn actionclasses are unstyled in EE7, which left gray browser-default buttons); the settings modal and picker are themed entirely via EE7's--ee-*CSS variables (no detection heuristics or hardcoded fallback colors), so they follow the active light/dark theme exactly - Build tooling: archiver 7 → 8
Removed
- Removed dead
ControlPanel/Routesclasses (stale duplicates of the mcp controller)
Fixed
- Fix: publish page for non-superadmin roles errored with "Unable to load requested publish tab file: tab.yumpu_embed.php" — the module was registered with
has_publish_fields = 'y'(a legacy publish-tab flag) even though the add-on ships a fieldtype, not a tab. The installer now registers with 'n' (existing dev installs need a reinstall). - Field-picker JSON endpoints (documents, templates) moved to front-end ACT actions in
mod.yumpu_embed.php, gated on entry-edit or addon-access permissions — EE blocks non-admins from allcp/addons/settings/...routes, so CP-routed endpoints could never work for editors. Running the add-on update (or a reinstall) registers the actions. - Exact pagination in the document picker (no phantom empty last page)
- Fieldtype JS moved to a shared initializer with a MutationObserver, so fields added to the DOM dynamically (Grid/Fluid rows) initialize correctly
Security
- Security: escape
</script>sequences when rendering player settings into the front-end embed script
0.2.0
Added
- Initial scaffold for Yumpu Embed (ExpressionEngine 7); document picker, player settings modal, player templates, JS Embed API rendering