=== NOW Finance Social Content Importer ===
Contributors: pelsnowfinance
Tags: tiktok, instagram, linkedin, social media, content import
Requires at least: 6.0
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 2.1.7
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Import content you administer from TikTok, Instagram, and LinkedIn into a configurable WordPress content library.

== Description ==

NOW Finance Social Content Importer reads posts and media from supported social APIs and stores them in WordPress. Imports include descriptions, hashtags, dates, and available engagement counts.

New imports are drafts. Automatic imports, public REST access, and local MP4 downloads are off by default. The responsive [`nsci_content_feed`] shortcode displays published items without requiring ACF, Elementor, or FileBird.

You supply and maintain each provider's developer app, credentials, account access, and approvals. Installing the plugin does not grant API access.

= Features =

* TikTok Display API imports with OAuth and token refresh.
* Instagram Graph API imports for supported professional accounts.
* LinkedIn Posts API imports for approved organization integrations and member integrations that already have access.
* Draft-first imports, source-ID deduplication, and update-only imports.
* Guarded image and optional video downloads.
* Manual imports, WP-Cron schedules, WP-CLI commands, and an administrator REST import route.
* A plugin-managed post type or an existing post type, with field mapping to ACF, JetEngine/Crocoblock, or WordPress metadata.
* Optional native field editor and responsive feed shortcode.

== Installation ==

1. Install the plugin ZIP from Plugins > Add New, or upload the now-finance-social-content-importer folder to /wp-content/plugins/.
2. Activate the plugin.
3. Open Social Content > Content to choose the plugin-managed destination or an existing post type and map its fields.
4. Open Social Content > Help to review provider requirements. Prepare your developer app and approved scopes, then enter its details under Social Content > Connections.
5. Save the provider settings. Connect the account for OAuth providers, then select Test Connection.
6. From Overview, run a manual import. Review the drafts in your chosen post type and inspect any errors under Logs.
7. Publish the posts you want to display, then add [`nsci_content_feed`] to a page.
8. Enable scheduled imports, public REST access, or local MP4 downloads only if your site needs them.

The server must have Sodium or OpenSSL for encrypted credential storage. To keep encryption stable if WordPress salts rotate, define a long random `NSCI_SECRET_KEY` in `wp-config.php`.

== Shortcodes ==

= Imported content feed =

[`nsci_content_feed`] displays published imported items in a responsive grid.

* `count`: 1 to 100 posts; default 12.
* `source`: Optional `tiktok`, `instagram`, or `linkedin` filter.
* `columns`: 1 to 4 columns; default 3.

Example: [`nsci_content_feed source="instagram" count="6" columns="2"`]

= Social slider and popup =

Add [`nsci_content_slider source="instagram,tiktok,linkedin" media="both" account="@nowfinance"`] to a page's Shortcode block or an Elementor Shortcode widget.

* `source`: Comma-separated `instagram`, `tiktok`, and/or `linkedin`; omitted means all three.
* `media`: `video`, `image`, or `both` (default).
* `count`: 1 to 100 posts; default 12. Only published, unprotected imported posts with media appear.
* `height`: Card height in pixels (200 to 1000); default 500. Images and videos fill the portrait card.
* `popup_height`: Dialog height; defaults to `85vh` (85% of the visible screen height). A pixel height from 400 to 1200 can override it, constrained to the screen.
* `account`: Profile name shown in the popup. Defaults to the TikTok handle from its post URL or the WordPress site name.
* `avatar`: Optional profile image URL. Otherwise an account initial is shown.
* `interval`: Time between one-card advances in milliseconds (1000 to 30000); default 3000.
* `duration`: Animation time in milliseconds (100 to 2000); default 600. Cards move left to right, one at a time.
* `instagram_profile`, `tiktok_profile`, `linkedin_profile`: Profile URLs for the popup account/avatar and platform icon; default to NOW Finance's matching profiles.
* `autoscroll`: `true` (default) or `false`. Hover/focus pauses automatic movement. Each mouse-wheel/trackpad burst, drag release, or horizontal swipe advances exactly one card. Positive wheel input and dragging right move cards left to right; reverse input moves them back. Reduced-motion preferences disable automatic scrolling, while muted video previews remain enabled. There are no slider navigation or pause buttons.

Examples:

* Videos only: [`nsci_content_slider source="instagram,tiktok" media="video" account="@nowfinance"`]
* Images only: [`nsci_content_slider source="instagram,linkedin" media="image" account="NOW Finance"`]
* All media: [`nsci_content_slider media="both" height="500" popup_height="85vh" account="@nowfinance"`]

Cards have 24px rounded corners and show a platform icon; hover or keyboard focus reveals the caption, likes, and comments. On touchscreens the caption is always visible. Select a card to open the scrollable popup with the full description, account, date, engagement, and original-platform link. Account/avatar and platform icons open the matching profile; likes, comments, and View on links open the original post. Escape, the centered close icon, or the backdrop closes it. Popup media fills its frame using cover cropping. The popup fits smaller screens and traps keyboard focus while open. Its details sit at the bottom, while the media grows to fill the remaining height. Long captions remain scrollable.

Videos play muted in the slider. Local videos continue in the popup using the same player and position; TikTok restores its last reported timestamp through the player API. A bottom-right mute icon toggles sound. Tap the video to pause or resume and use the timeline to seek. Swipe up or down over popup media or text to scroll its content. Native file videos use browser timeline controls; TikTok uses plugin playback and timeline controls so vertical swipes stay within the popup.

Instagram and LinkedIn videos require a playable MP4. Enable local MP4 downloads, then run Resolve Videos to populate `video_mp4_url`/`video_mp4_attachment_id`. Resolve Videos also repairs mapped fields when a downloaded attachment already exists. The slider prefers managed local attachments over provider URLs, supports file-array/attachment-ID field formats, and recovers TikTok players from video permalinks if old IDs or media classifications are missing. Existing direct video files in `video_url` are also supported. TikTok uses its official embedded player when no local file exists. Videos without a playable source display their cover and original-platform link. Browser autoplay policies, unavailable posts, and provider restrictions can prevent playback. Images stay static. Multiple sliders can use different filters on the same page.

If Instagram covers appear but videos do not play, run Resolve Videos to replace expired provider CDN links with local MP4s. Clear the site's page/CDN cache after updating the plugin or resolving media so cached shortcode HTML does not retain old players or video URLs.

TikTok players contact TikTok when a preview becomes visible or a popup opens. Other media use the stored media URLs. No third-party player SDK is loaded. See [TikTok's player documentation](https://developers.tiktok.com/doc/embed-player/) for supported controls.

= Single content field =

[`nsci_content_field field="short_description"`] displays one mapped field.

* `field`: Required registered field key.
* `post_id`: Optional destination post ID; defaults to the current post.
* `fallback`: Optional text shown when the field is empty.

Example: [`nsci_content_field field="like_count" post_id="123" fallback="0"`]

Field mappings change where values are stored; registered shortcode field keys stay the same.

== Frequently Asked Questions ==

= Does the plugin provide API credentials? =

No. You create or control the developer app, obtain provider approval and scopes, and supply the credentials.

= What provider access do I need? =

TikTok requires approved Login Kit and Display API access with the requested scopes. Instagram imports require a supported professional account, Meta API access, a long-lived access token, and that account's Instagram User ID. LinkedIn organization imports require approved API access. LinkedIn currently says it is not accepting new requests for `r_member_social`, so member imports work only when that permission is already granted to your app. See the provider setup links under Social Content > Help.

= Why does Test Connection fail? =

Check that the provider approved the app and scopes, the credentials belong to the intended app, and the account ID or Author URN matches the account you want to import. For Instagram, use the API host and permissions that match your Meta login method. For TikTok and LinkedIn, register the exact HTTPS redirect URL shown in the plugin settings. Review Logs for the provider's response.

= Does it require ACF, JetEngine, Elementor, or FileBird? =

No. The plugin can use its own post type and WordPress metadata, or map fields to an existing post type, ACF, or JetEngine/Crocoblock. FileBird can organize imported attachments.

Some field types cannot be mapped directly: an image URL cannot fill an ACF Image field expecting an attachment ID. Repeater, group, relationship, and date conversions are not automatic. Review fields marked Type unknown before importing.

= Where do imports appear? =

Imports go to the selected destination and are drafts by default. Changing the destination moves previously imported items there on their next import or "Update existing" run. Changing a field mapping only affects values written from then on; values already stored under the old key are not copied. Check Logs for import results.

= How do I display imported content? =

Use [`nsci_content_feed`]. Its optional attributes are `count` (1 to 100), `source` (`tiktok`, `instagram`, or `linkedin`), and `columns` (1 to 4). Use [`nsci_content_field field="short_description"`] to display one registered field.

= Does the plugin contact providers after activation? =

No. Automatic imports are off on new installations. Provider requests begin when an administrator connects an account, tests a connection, or starts an import.

= Are scheduled imports exact-time jobs? =

Schedules use WP-Cron, which runs when WordPress receives a request. Low-traffic sites may run jobs late; a server cron can call `wp-cron.php` on a regular schedule.

= What happens when the plugin is deleted? =

Credentials, settings, caches, import history, and scheduled events are removed. Imported posts and media are retained. Delete those separately if you want a full purge.

== External services ==

The plugin contacts a provider only after an administrator connects an account, tests a connection, or starts an import. Depending on the provider and action, it sends access tokens, account or author identifiers, requested content fields, paging cursors, or OAuth data. It receives posts, media URLs, account data, engagement counts, and token responses. The plugin developer receives no telemetry.

= TikTok =

Used for Login Kit OAuth, token management, profile checks, and reading the connected user's public videos through the Display API.

* Service: https://developers.tiktok.com/
* Developer Terms: https://www.tiktok.com/legal/page/global/tik-tok-developer-terms-of-service/en
* Terms of Service: https://www.tiktok.com/legal/page/global/terms-of-service/en
* Privacy Policy: https://www.tiktok.com/legal/page/row/privacy-policy/en

= Meta / Instagram =

Used to read media and account data for a configured professional Instagram account through the selected Instagram Graph API host.

* Service: https://developers.facebook.com/docs/instagram-platform/
* Platform Terms: https://developers.facebook.com/terms/
* Privacy Policy: https://www.facebook.com/privacy/policy/

= LinkedIn =

Used to read posts, media assets, and social-action counts for the configured LinkedIn author. Community Management API access and permissions are controlled by LinkedIn.

* Service: https://learn.microsoft.com/en-us/linkedin/marketing/community-management/community-management-overview
* App review: https://learn.microsoft.com/en-us/linkedin/marketing/community-management/community-management-app-review
* API Terms of Use: https://www.linkedin.com/legal/l/api-terms-of-use
* User Agreement: https://www.linkedin.com/legal/user-agreement
* Privacy Policy: https://www.linkedin.com/legal/privacy-policy

== Privacy ==

Credentials and tokens are stored encrypted in the WordPress database. Imported posts, provider identifiers, import history, and downloaded media are stored locally. Public REST access is off by default. When enabled, published imported content can be read through the plugin endpoint and, for supported post types, the WordPress core REST endpoint.

Site owners are responsible for disclosing connected providers, imported content, retention, and public presentation in their privacy policy. The plugin adds suggested wording to the WordPress Privacy Policy Guide. Only import and locally copy media you are authorized to use; provider terms and access policies still apply.

== Changelog ==

= 2.1.7 =
* Anchor popup details to the bottom and let media grow into the remaining height.

= 2.1.6 =
* Size popups to 85% of the visible screen height by default.

= 2.1.5 =
* Wait for TikTok player readiness before sending playback commands.
* Show persistent video recovery results with actionable provider errors.
* Recognize mapped video categories when repairing legacy Instagram imports.

= 2.1.4 =

* Hide popup scrollbars while preserving wheel, touch, and keyboard scrolling.

= 2.1.3 =

* Advance exactly one card per wheel/trackpad burst, mouse drag, or touch swipe.
* Keep visible automatic movement left to right and prevent theme smooth-scrolling from reversing loop resets.
* Recover TikTok players from post permalinks and prefer resolved local video attachments.
* Repair stale mapped video fields when Resolve Videos reuses an existing download.
* Initialize muted video sources explicitly and keep autoplay separate from carousel motion preferences.

= 2.1.2 =

* Use 24px corners and advance one card every 3000ms with a 600ms animation.
* Continue video playback in popups, add bottom-right mute controls, and center the close icon.
* Fill popup media using cover cropping and add tap-to-pause, seeking, and vertical swipe scrolling.
* Link account details to platform profiles and engagement counts to the original post.

= 2.1.1 =

* Prevent theme and Elementor button styles from covering card media.
* Remove slider controls and add wheel, trackpad, mouse drag, and touch swipe navigation.
* Load slider assets before Elementor preview/widget rendering and initialize refreshed widgets.
* Bound icons before CSS loads and clean up removed editor instances.

= 2.1.0 =

* Add the configurable nsci_content_slider shortcode with platform and image/video filters.
* Add 500px portrait cards, muted video previews, continuous scrolling, captions, and engagement overlays.
* Add responsive 800px media popups with sound controls, account details, and platform links.
* Add keyboard navigation, focus management, reduced-motion support, and lazy media playback.

= 2.0.7 =

* Protect password-locked media and captions in feeds and core REST metadata.
* Serialize import starts, re-read cursors under the lock, and preserve newer lock owners during renewal and release.
* Consume OAuth state once under a per-flow lock and keep unknown callbacks free of writes.
* Disable authenticated provider redirects and reject credentialed or nonstandard-port media and paging URLs.
* Redact JSON credentials and notices, and invalidate LinkedIn tokens when the client secret changes.
* Apply field mapping changes immediately and prefer live imports when a trashed duplicate exists in another destination.

= 2.0.6 =

* Make import, token-refresh, and video-resolve locks atomic so overlapping requests cannot run the same batch twice.
* Bind OAuth callbacks to the browser that started the connection, and redirect on unknown or expired state.
* Refresh Instagram Login tokens before they expire, and skip items that repeatedly fail a batch.
* Pick the video and thumbnail from Instagram carousel slides, and stop single images using up the post count in "videos" mode.
* Prefer a live imported post over a trashed duplicate, and stop a cancelled import from resuming.
* Hide password-protected and non-imported posts from REST and field shortcodes.
* Avoid loading translations early during upgrades, and translate all admin strings.

= 2.0.5 =

* Find imported items in any post type so changing the destination moves them instead of creating duplicates.
* Explain on save that previously imported items move to a new destination on the next import or update run.

= 2.0.4 =

* Wait with backoff while another import batch holds the lock instead of retrying continuously.
* Make cancellation stop in-progress imports, and report imports that finished before they could be cancelled.
* Tell administrators when starting an import continues one already in progress.
* Prevent two requests from taking over an expired import lock at the same time.
* Show interrupted imports as continuing in the background instead of finished.

= 2.0.3 =

* Add a visible Help tab with clearer provider setup and access requirements.
* Shorten and clarify the installation guide and FAQs.
* Default invalid import statuses to draft instead of publish.
* Keep stored LinkedIn counts and media when provider lookups fail during update runs.
* Skip trashed imports instead of re-creating them.
* Limit the feed shortcode, public REST endpoint, and imported count to imported items.

Older release notes are in changelog.txt in the plugin folder.

== Upgrade Notice ==

= 2.1.7 =
Popup media expands to fill unused height above the bottom-aligned details.

= 2.1.6 =
Popups now default to 85% of the visible screen height.

= 2.1.5 =
Fixes TikTok player startup messages and reports Instagram video recovery failures.

= 2.1.4 =

Hides the popup scrollbar while keeping long descriptions scrollable by wheel, swipe, and keyboard.

= 2.1.3 =

Fixes one-card gesture navigation, left-to-right animation, and legacy video lookup. Run Resolve Videos for expired Instagram links, then clear the site cache.

= 2.1.2 =

Adds one-card timed scrolling, rounded cards, cover media, continuing popup playback, mute/timeline controls, profile/post links, and vertical popup swipe scrolling.

= 2.1.1 =

Fixes opaque slider cards and Elementor editor rendering. Navigate with the mouse wheel, trackpad, drag, or swipe; slider buttons are removed. Existing shortcodes continue to work.

= 2.1.0 =

Adds a configurable social slider and media popup. Add [nsci_content_slider account="@nowfinance"] to a page. Enable local MP4 downloads and run Resolve Videos for Instagram/LinkedIn playback.

= 2.0.7 =

Security and reliability fixes for password-protected content, OAuth, import concurrency, and credential handling. Existing settings and imported content are preserved.

= 2.0.6 =

Reliability and security fixes for imports, OAuth connections, and Instagram carousels. Existing settings and imported content are preserved.

= 2.0.5 =

Changing the content destination no longer duplicates imports. Run "Update existing" after changing it to move all previously imported items.

= 2.0.4 =

Makes the import panel's cancel and progress reporting reliable when imports overlap. Existing settings and imported content are preserved.

= 2.0.3 =

Setup Help is available from the Social Content navigation. Existing settings and imported content are preserved.

= 2.0.0 =

When migrating from the previous plugin slug, back up the database, deactivate the old plugin, and do not delete it because its uninstall routine removes shared settings and credentials. Update TikTok and LinkedIn redirect URLs, then restart OAuth flows.
