{"id":321859,"date":"2026-06-08T02:25:43","date_gmt":"2026-06-08T02:25:43","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/lihi-short-url\/"},"modified":"2026-08-03T01:45:25","modified_gmt":"2026-08-03T01:45:25","slug":"lihi-short-url","status":"publish","type":"plugin","link":"https:\/\/pcm.wordpress.org\/plugins\/lihi-short-url\/","author":23496059,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.0.6","stable_tag":"1.0.6","tested":"7.0.2","requires":"5.5","requires_php":"7.4","requires_plugins":null,"header_name":"lihi Short URL","header_author":"lihi","header_description":"Adds a one-click \"lihi\" button to generate and copy short URLs, including posts, pages, media and all post-type list tables.","assets_banners_color":"","last_updated":"2026-08-03 01:45:25","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/lihi.io","rating":0,"author_block_rating":0,"active_installs":0,"downloads":220,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.1":{"tag":"1.0.1","author":"lihidev","date":"2026-06-08 02:55:30"},"1.0.4":{"tag":"1.0.4","author":"lihidev","date":"2026-06-25 12:37:28"},"1.0.5":{"tag":"1.0.5","author":"lihidev","date":"2026-06-26 01:21:22"},"1.0.6":{"tag":"1.0.6","author":"lihidev","date":"2026-08-03 01:45:25"}},"upgrade_notice":{"1.0.6":"<p>Adds PKCE Login, rotating access \/ refresh credentials, work-group switching, and stricter API failure handling. Existing saved credentials are not migrated; log in again after updating.<\/p>","1.0.5":"<p>Fixes Plugin Check security findings for admin AJAX parsing and output escaping; no action required.<\/p>","1.0.4":"<p>Adds modal short-URL creation options, JS-rendered Copy\/Edit states, lihi dashboard passthrough, password-based account verification, and current lihi API option handling; no action required.<\/p>","1.0.3":"<p>Unifies the lihi API client internals and clears saved plugin data on deactivation; no action required.<\/p>","1.0.2":"<p>Strengthens authentication site identity verification; no action required.<\/p>","1.0.1":"<p>Updates WordPress.org release metadata, package paths, and authentication site identity payload; no action required.<\/p>","1.0.0":"<p>Initial release.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3586169,"resolution":"128x128","location":"assets","locale":"","width":251,"height":245}},"assets_banners":[],"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.1","1.0.4","1.0.5","1.0.6"],"block_files":[],"assets_screenshots":[],"screenshots":[]},"plugin_section":[],"plugin_tags":[83,266119,84,137,5530],"plugin_category":[50],"plugin_contributors":[266120],"plugin_business_model":[],"class_list":["post-321859","plugin","type-plugin","status-publish","hentry","plugin_tags-admin","plugin_tags-lihi","plugin_tags-media","plugin_tags-short-url","plugin_tags-url-shortener","plugin_category-media","plugin_contributors-lihidev","plugin_committers-lihidev"],"banners":[],"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/lihi-short-url\/assets\/icon-128x128.png?rev=3586169","icon_2x":false,"generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p>lihi Short URL integrates the <a href=\"https:\/\/lihi.io\">lihi<\/a> short-link service into the WordPress admin. Editors can create short URLs from post and media list screens, choose a redirect domain, add tags, and add UTM parameters for non-media items, then copy the result without leaving WordPress. Existing short URLs become Copy controls, and administrators can open the matching lihi dashboard page to edit the link. This plugin is open source and maintained at <a href=\"https:\/\/github.com\/weedgood\/lihi-wp-plugin\">weedgood\/lihi-wp-plugin<\/a>.<\/p>\n\n<p>The plugin runs only inside <code>wp-admin<\/code>; it adds no front-end output and enqueues no scripts on public pages.<\/p>\n\n<h4>Features<\/h4>\n\n<ul>\n<li>Adds a <strong>lihi Short URL<\/strong> column with a <strong>Create<\/strong> button to all public post-type list screens (posts, pages, custom post types).<\/li>\n<li>Adds the same Create \/ Copy controls to the Media Library list view and to the attachment detail panel in the media grid view.<\/li>\n<li>One-click copy: generates the short URL on demand via AJAX and writes it to the clipboard, with a manual-copy prompt if browser clipboard access is blocked.<\/li>\n<li>Creation modal: choose a redirect domain, add recommended or custom tags, and add UTM parameters for non-media items before creating a new short URL.<\/li>\n<li>UTM source and medium are loaded from the lihi account options, while campaign, term, and content remain free-text fields; media items hide UTM controls and submit blank UTM values.<\/li>\n<li>Reuses an existing short URL whenever one already exists for the item, so repeated clicks are idempotent.<\/li>\n<li>Copy buttons still confirm the upstream short URL exists before copying; if it was removed, the button returns to <strong>Create<\/strong> and opens the creation modal again.<\/li>\n<li>Marks items with <code>lihi_already = 1<\/code> post meta after a successful short-URL lookup\/create; the frontend renders those buttons as \"Copy\".<\/li>\n<li>Administrators can open existing short URLs, personal domain management, and UTM option management in the lihi dashboard through a browser-proof passthrough flow.<\/li>\n<li>Settings page under <strong>Settings \u2192 lihi Short URL<\/strong> with Login and Register tabs, connected-account details, work-group switching, Logout, and a lihi dashboard shortcut. Login is selected by default, and the service heading breaks cleanly between its two sentences. Directly sanitized query-backed tab links and native WordPress AJAX POST forms keep tab switching and submission usable without JavaScript; JavaScript adds instant keyboard tabs, an accessible work-group modal layered above the dashboard workspace, inline errors, and delayed success feedback. Successful work-group changes reload the settings page after the confirmation so all group-scoped state is fresh. Logout makes one short best-effort server-session revocation and always continues with local credential cleanup.<\/li>\n<li>Registration checks the request country before sending a verification email. Allowed registrations retain the registration request's IP and device metadata for account creation; clicking the verification link does not replace them. After verifying, the administrator returns to the separate Login form; registration never logs the account in.<\/li>\n<li>Login uses server-side PKCE and stores the email, opaque server-issued session identifier, access token, and rotating refresh token together in one non-autoloaded WordPress option. A renewable 20-second database lease serializes Login and Refresh; only the current byte-exact lease owner can write credentials under the captured activation generation.<\/li>\n<li>Every refreshable protected lihi API request can refresh a rejected access token and retry once. Login and Refresh recheck activation after each remote response before renewing the lease; concurrent requests wait up to 18 seconds for a 15-second HTTP operation to finish. Any attempted refresh failure removes credentials still belonging to that Login session and asks the administrator to sign in again, while a newer Login remains untouched.<\/li>\n<li>Localised; ships with Traditional Chinese (<code>zh_TW<\/code>).<\/li>\n<\/ul>\n\n<h3>External services<\/h3>\n\n<p>This plugin connects to the lihi short URL service to identify the WordPress site, authenticate the site administrator, and create or look up short URLs. Without an internet connection the plugin cannot function.<\/p>\n\n<p><strong>Service: lihi WordPress API auth endpoints<\/strong> (https:\/\/app.lihi.com\/api\/wordpress\/v1\/auth)<\/p>\n\n<ul>\n<li>When data is sent: when an administrator submits Register, Login, or Logout on <strong>Settings \u2192 lihi Short URL<\/strong>; when Login exchanges its short-lived authorization code; when any protected API rejects the access token and the plugin attempts one token refresh; and once during plugin uninstall when a local session is available.<\/li>\n<li>What is sent for Register: the entered email and password plus the WordPress site's hostname. The request also carries normal network metadata such as source IP and User-Agent, which lihi uses to check registration availability and record the registration country and device. Account-creation consent is checked locally before the request. A successful registration only sends a verification email; it returns no login credentials and does not connect the plugin.<\/li>\n<li>What is sent for Login: the entered email and password, the WordPress site's hostname, and a PKCE challenge generated by WordPress PHP. The server returns a short-lived authorization code; WordPress sends that code with the matching verifier retained only in PHP memory to exchange it for a server-issued UUID, access token, and refresh token.<\/li>\n<li>What is sent for refresh: the stored server-issued UUID and current refresh token. A successful refresh returns a new access token and a rotated refresh token.<\/li>\n<li>What is sent for Logout or uninstall: the stored access token in the Authorization header. No request body is sent. This one-shot request is not refreshed or retried; network or API failure is ignored so local Logout\/uninstall cleanup still finishes.<\/li>\n<li>What WordPress stores: one non-autoloaded credential option containing the email, UUID, access token, and raw refresh token. The password, PKCE verifier, challenge, and authorization code are not stored after the request.<\/li>\n<\/ul>\n\n<p><strong>Service: lihi WordPress API protected endpoints<\/strong> (https:\/\/app.lihi.com\/api\/wordpress\/v1)<\/p>\n\n<ul>\n<li>When data is sent: when a connected administrator opens the settings page to display account information, opens the work-group switcher, or confirms a work-group change; when the Create modal loads redirect-domain and UTM options; when an administrator opens the lihi dashboard through passthrough; and when a user clicks \"Create\", \"Copy\", or \"Edit\" to generate, look up, copy, or edit a short URL. Media Create modals hide UTM controls and submit blank UTM values.<\/li>\n<li>What is sent: the stored access token; when switching work groups, the selected numeric group ID or <code>null<\/code> for the personal work group; the post or attachment URL (<code>permalink<\/code> or attachment file URL, with entered UTM parameters appended); the post type namespace including the WordPress hostname; the post ID; the selected redirect domain; selected tags as a comma-separated string; and, when requesting browser passthrough, a browser-generated PKCE challenge plus an optional target such as a short URL or lihi dashboard path.<\/li>\n<\/ul>\n\n<p><strong>Service: browser-facing lihi pages<\/strong> (https:\/\/lihi.io, https:\/\/app.lihi.com, and https:\/\/lihidomain.com)<\/p>\n\n<ul>\n<li>When data is sent: only after a user clicks the lihi dashboard, password-reset, personal-domain, or verification-email link.<\/li>\n<li>What is sent: the browser's normal request metadata. Connected dashboard links additionally carry the short-lived passthrough nonce and browser verifier described above; verification links carry the one-time registration token from the email. Public home, password-reset, and public domain-information links receive no account credentials from the plugin.<\/li>\n<\/ul>\n\n<p>By using the plugin you agree that the data above is transmitted to the lihi service. Please review the lihi service's legal documents:<\/p>\n\n<ul>\n<li>Terms of Use: https:\/\/knowledge.lihi.io\/terms\/<\/li>\n<li>Privacy Policy: https:\/\/knowledge.lihi.io\/privacy-policy\/<\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>lihi-short-url<\/code> folder to the <code>\/wp-content\/plugins\/<\/code> directory, or install the plugin through the WordPress <strong>Plugins<\/strong> screen.<\/li>\n<li>Activate the plugin through the <strong>Plugins<\/strong> screen.<\/li>\n<li>Go to <strong>Settings \u2192 lihi Short URL<\/strong>.<\/li>\n<li>If you already have a verified lihi account, enter its email and password in <strong>Login<\/strong>. If you need an account, use the separate <strong>Register<\/strong> form, confirm account creation, open the verification email, then return and log in.<\/li>\n<li>Once Login has stored a complete credential bundle, the <strong>Create<\/strong> button appears in a <strong>lihi Short URL<\/strong> column on every public post-type list screen and in the Media Library.<\/li>\n<li>To use another available lihi work group, click <strong>Switch<\/strong> beside the current work group, choose it in the modal, and click <strong>Switch work group<\/strong>.<\/li>\n<\/ol>\n\n<p>The plugin requires the <code>manage_options<\/code> capability to view or change settings. Any logged-in user can use the <strong>Create<\/strong> button on screens they are otherwise allowed to access.<\/p>\n\n<p>Serve <code>wp-admin<\/code> over HTTPS before using Login or Register. Passwords first travel from the browser to same-origin WordPress AJAX, then WordPress calls lihi over HTTPS. The plugin does not hard-block non-HTTPS requests because TLS may terminate at a correctly configured reverse proxy.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"why%20don%27t%20i%20see%20the%20create%20button%20in%20my%20list%20tables%3F\"><h3>Why don't I see the Create button in my list tables?<\/h3><\/dt>\n<dd><p>The UI hooks register only after Login has stored a complete email, UUID, access-token, and refresh-token bundle. Open <strong>Settings \u2192 lihi Short URL<\/strong> and log in with a verified account. Registering or clicking the verification link alone does not log in.<\/p><\/dd>\n<dt id=\"how%20do%20i%20connect%20a%20different%20account%3F\"><h3>How do I connect a different account?<\/h3><\/dt>\n<dd><p>Click <strong>Log out<\/strong> to remove the local credential bundle, then use Login with the other account. There is no separately editable saved-email field.<\/p><\/dd>\n<dt id=\"can%20i%20manage%20redirect%20domains%20or%20utm%20options%20from%20wordpress%3F\"><h3>Can I manage redirect domains or UTM options from WordPress?<\/h3><\/dt>\n<dd><p>Yes. Administrators can open lihi personal-domain and UTM option management from the Create modal. The plugin asks for confirmation, creates a short-lived passthrough nonce, then opens the lihi dashboard in a new tab.<\/p><\/dd>\n<dt id=\"how%20do%20i%20disable%20the%20plugin%20without%20deactivating%20it%3F\"><h3>How do I disable the plugin without deactivating it?<\/h3><\/dt>\n<dd><p>Click <strong>Log out<\/strong> on <strong>Settings \u2192 lihi Short URL<\/strong>. This removes the local credential bundle and stops registering the Short URL UI. Deactivation performs one complete transition: disable the generation before waiting, acquire the shared lock, repeat disable and credential purge under that lock, then release. If transition cleanup fails, isolated best-effort cleanup keeps authentication fail-closed and never directly deletes another request's lock.<\/p><\/dd>\n<dt id=\"does%20the%20plugin%20run%20on%20the%20front-end%3F\"><h3>Does the plugin run on the front-end?<\/h3><\/dt>\n<dd><p>No. The plugin returns early on non-admin requests \u2014 it only adds admin UI and an <code>admin-ajax.php<\/code> handler.<\/p><\/dd>\n<dt id=\"which%20post%20types%20are%20supported%3F\"><h3>Which post types are supported?<\/h3><\/dt>\n<dd><p>All post types registered with <code>public =&gt; true<\/code>, plus the Media Library (both list mode and the grid view's attachment details panel).<\/p><\/dd>\n<dt id=\"does%20the%20plugin%20store%20data%20in%20my%20database%3F\"><h3>Does the plugin store data in my database?<\/h3><\/dt>\n<dd><p>Yes \u2014 one non-autoloaded <code>lihi_auth_tokens<\/code> option containing <code>{ email, uuid, access_token, refresh_token }<\/code>, a non-autoloaded renewable 20-second <code>lihi_auth_tokens_lock<\/code>, one non-autoloaded random <code>lihi_auth_epoch<\/code>, and per-item <code>lihi_already<\/code> post meta. Credential writes atomically require the exact epoch and lease owner; cleanup uses a byte-exact raw stored value. Normal credential probes are memoized for the PHP request, while Login\/Refresh concurrency checks always read fresh database state. Store failures during bootstrap probes degrade to disconnected instead of breaking wp-admin. The lihi password is never stored. Logging out removes the credential bundle. Deactivating or deleting the plugin removes its authentication options; if another request still owns the lease, cleanup disables authentication without force-deleting that foreign lock. Per-item <code>lihi_already<\/code> post meta remains attached to its item.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.0.6<\/h4>\n\n<ul>\n<li>Adds separate Login and Register forms; Register sends verification only and never logs in.<\/li>\n<li>Adds server-side PKCE authorization-code exchange, one atomic email\/UUID\/access\/refresh credential bundle, rotating refresh tokens, and one access fallback retry for every refreshable protected API.<\/li>\n<li>Adds one renewable 20-second database-backed auth lease around complete Login and Refresh flows plus local Logout, with owner-only renewal between remote steps and 18-second waiter budgets.<\/li>\n<li>Revokes the server-issued WordPress client on Logout and uninstall with a one-shot five-second request. Uninstall fences and drains Login\/Refresh first, then performs revocation and local purge under the same lifecycle lease; every remote failure remains best-effort so cleanup always continues.<\/li>\n<li>Adds activation-generation fencing, response-time epoch rechecks, and lock-aware lifecycle cleanup so stale requests cannot restore credentials after deactivation or rapid reactivation.<\/li>\n<li>Keeps Login and Register usable as native WordPress AJAX POST forms without JavaScript, with Unicode-consistent password validation and accessible inline errors when JavaScript is available.<\/li>\n<li>Presents Login and Register as mutually exclusive tabs with Login selected by default, and adds an accessible modal for loading and switching lihi work groups.<\/li>\n<li>Replaces profile end dates with nullable work-group names, validates profile field types before rendering, and updates the protected API contract to use <code>\/user\/domain-options<\/code>, <code>\/user\/group-options<\/code>, and <code>\/user\/switch-group<\/code>.<\/li>\n<li>Sends the WordPress hostname with Login, reports unavailable registration countries with translated plugin copy before mail is sent, and keeps registration-time country, IP, and device metadata through email verification.<\/li>\n<li>Gives <code>site\/store<\/code> <code>need_upgrade<\/code> failures a dedicated translated upgrade-or-renew message while keeping <code>site_create_fail<\/code> on the normal validation-error path, and classifies every HTTP 5xx response as a service failure before inspecting identity markers.<\/li>\n<li>Hardens concurrent authentication storage with request-memoized helper reads, fresh concurrency reads, guarded atomic credential upserts, <code>CAST(... AS BINARY)<\/code> raw-value cleanup, consistent database error handling, malformed-row repair, scoped non-autoloaded option-cache invalidation without evicting <code>alloptions<\/code>, and fail-closed bootstrap\/lifecycle behavior.<\/li>\n<li>Sanitizes the read-only authentication-tab query directly, improves the service-heading sentence break, keeps the work-group modal above later dashboard content, and reloads the settings page after successful work-group switches.<\/li>\n<\/ul>\n\n<h4>1.0.5<\/h4>\n\n<ul>\n<li>Fixes Plugin Check security findings around admin AJAX request parsing and escaped output.<\/li>\n<\/ul>\n\n<h4>1.0.4<\/h4>\n\n<ul>\n<li>Adds JavaScript-rendered Create, Copy, and administrator-only Edit controls for the lihi Short URL column.<\/li>\n<li>Adds a creation modal with redirect-domain selection, click-to-add recommended tags, custom tags, UTM source \/ medium options, and free-text UTM fields for non-media items.<\/li>\n<li>Verifies existing short URLs before Copy and Edit; removed upstream links reset the item back to Create and reopen the creation flow.<\/li>\n<li>Adds clipboard-blocked fallback prompts so generated short URLs remain available for manual copy.<\/li>\n<li>Adds lihi dashboard passthrough for editing short URLs, managing personal domains, and managing UTM options.<\/li>\n<li>Updates the settings page with password-based email verification, account-creation consent, connected-account details, and a lihi dashboard service overview.<\/li>\n<li>Aligns the lihi WordPress API client with the current <code>site\/find<\/code> single-result response and <code>user\/options<\/code> domain \/ UTM options response.<\/li>\n<\/ul>\n\n<h4>1.0.3<\/h4>\n\n<ul>\n<li>Unifies authentication and short-URL calls under the lihi WordPress API client.<\/li>\n<li>Updates internal dependency composition for client, service, and store singletons.<\/li>\n<li>Clears saved settings, site UUID, and cached token when the plugin is deactivated.<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>Strengthens authentication identity checks by sending the site hostname and persistent site UUID in the authentication JSON payload instead of relying on the HTTP Host header.<\/li>\n<li>Sends WordPress' mobile-request flag (<code>is_mobile<\/code>) on auth login requests.<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Aligns the plugin package directory, main file, and text domain with the WordPress.org slug.<\/li>\n<li>Removes dashboard-wide setup notices while keeping the settings page available.<\/li>\n<li>Updates release packaging validation for the <code>lihi-short-url<\/code> directory.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release.<\/li>\n<li>Adds a \"lihi\" short-URL button to all public post-type list tables and the Media Library.<\/li>\n<li>Settings page with email verification flow and per-account redirect domain selection.<\/li>\n<li>Traditional Chinese (<code>zh_TW<\/code>) translation included.<\/li>\n<\/ul>","raw_excerpt":"Adds lihi Short URL controls to create, copy, and edit short URLs for posts, pages, media, and public post types in the WordPress admin.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/pcm.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/321859","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pcm.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/pcm.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/pcm.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=321859"}],"author":[{"embeddable":true,"href":"https:\/\/pcm.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/lihidev"}],"wp:attachment":[{"href":"https:\/\/pcm.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=321859"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/pcm.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=321859"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/pcm.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=321859"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/pcm.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=321859"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/pcm.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=321859"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/pcm.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=321859"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}