{"result":{"depiction_url":null,"edges":{"in_module":{"objects":[{"created":"2026-10-13T15:15:56Z","object_id":{"id":3085,"is_a":["text","documentation","reference","module"],"name":"doc_module_mod_import_feed","title":"mod_import_feed","uri":"https:\/\/zotonic.com\/id\/3085"},"seq":1}],"predicate":{"id":333,"is_a":["meta","predicate"],"name":"in_module","title":{"_type":"trans","tr":{"en":"In module"}},"uri":"https:\/\/zotonic.com\/id\/in_module"}},"subject":{"objects":[{"created":"2026-10-13T15:15:56Z","object_id":{"id":2555,"is_a":["categorization","keyword","keyword_information_type"],"name":"zotonic_topic_reference","title":"Reference","uri":"https:\/\/zotonic.com\/id\/2555"},"seq":1},{"created":"2026-10-13T15:15:56Z","object_id":{"id":2564,"is_a":["categorization","keyword","keyword_audience"],"name":"zotonic_topic_backend_developer","title":"Backend developer","uri":"https:\/\/zotonic.com\/id\/2564"},"seq":2},{"created":"2026-10-13T15:15:56Z","object_id":{"id":2600,"is_a":["categorization","keyword","keyword_domain"],"name":"zotonic_topic_import_and_migration","title":"Import and migration","uri":"https:\/\/zotonic.com\/id\/2600"},"seq":3},{"created":"2026-10-13T15:15:56Z","object_id":{"id":2625,"is_a":["categorization","keyword","keyword_architecture"],"name":"zotonic_topic_model","title":"Model","uri":"https:\/\/zotonic.com\/id\/2625"},"seq":4},{"created":"2026-10-13T15:15:56Z","object_id":{"id":2650,"is_a":["categorization","keyword","keyword_task"],"name":"zotonic_topic_import","title":"Import","uri":"https:\/\/zotonic.com\/id\/2650"},"seq":5},{"created":"2026-10-13T15:15:56Z","object_id":{"id":2665,"is_a":["categorization","keyword","keyword_data_type"],"name":"zotonic_topic_structured_data","title":"Structured data","uri":"https:\/\/zotonic.com\/id\/2665"},"seq":6},{"created":"2026-10-13T15:15:56Z","object_id":{"id":2667,"is_a":["categorization","keyword","keyword_data_type"],"name":"zotonic_topic_url","title":"URL","uri":"https:\/\/zotonic.com\/id\/2667"},"seq":7}],"predicate":{"id":308,"is_a":["meta","predicate"],"name":"subject","title":{"_type":"trans","tr":{"en":"Keyword"}},"uri":"http:\/\/purl.org\/dc\/elements\/1.1\/subject"}}},"id":3087,"is_a":["text","documentation","reference","model"],"links":[{"rel":"self","target":"https:\/\/zotonic.com\/.zotonic\/websub\/topic\/3087"},{"rel":"hub","target":"https:\/\/zotonic.com\/.zotonic\/websub"}],"medium":null,"medium_url":null,"name":"doc_model_model_import_feed","page_url":{"en":"https:\/\/zotonic.com\/docs\/3087\/import_feed","x-default":"https:\/\/zotonic.com\/docs\/3087\/import_feed"},"preview_url":null,"resource":{"version":20,"pivot_location_lat":null,"title":"import_feed","is_authoritative":true,"body":"<p>Discover, preview and manage the feeds imported by <code>mod_import_feed<\/code>.<\/p>\n<p>Administrator accounts cannot run imports. Select an enabled non-administrator account\nwith only the required import permissions. This is checked when saving settings and\nbefore polling, including for existing feeds whose account is later promoted to admin.<\/p>\n<h2>Model paths<\/h2>\n<p>The model exposes read-only GET paths. All paths require <code>use<\/code> permission for\n<code>mod_import_feed<\/code>; denied requests return <code>{error, eacces}<\/code>. Unknown paths return\n<code>{error, unknown_path}<\/code>. Successful <code>m_get\/3<\/code> calls return <code>{ok, {Value, Rest}}<\/code>.<\/p>\n<table class=\"table\"><thead><tr><th>Template expression<\/th><th>GET path<\/th><th>Value<\/th><\/tr><\/thead><tbody><tr><td><code>m.import_feed.can_select_user<\/code><\/td><td><code>can_select_user<\/code><\/td><td>Whether the caller may choose an import account.<\/td><\/tr><tr><td><code>m.import_feed.list<\/code><\/td><td><code>list<\/code><\/td><td>Feed maps, newest first, including statistics and <code>article_count<\/code>.<\/td><\/tr><tr><td><code>m.import_feed.get[id]<\/code><\/td><td><code>get\/&lt;id&gt;<\/code><\/td><td>One feed map; <code>enoent<\/code> when missing.<\/td><\/tr><tr><td><code>m.import_feed.items[id]<\/code><\/td><td><code>items\/&lt;id&gt;<\/code><\/td><td>First page of import history, up to 100 rows.<\/td><\/tr><tr><td><code>m.import_feed.history[id][offset]<\/code><\/td><td><code>history\/&lt;id&gt;\/&lt;offset&gt;<\/code><\/td><td>Map with <code>items<\/code>, <code>offset<\/code>, <code>previous<\/code>, <code>next<\/code> and <code>has_next<\/code>.<\/td><\/tr><\/tbody><\/table>\n<p>Feed maps use atom keys and include settings, counters, poll times and <code>is_running<\/code>.\n<code>is_zotonic_feed<\/code> records detected native resource entries: true for native or mixed\nfeeds, false for ordinary feeds, and undefined until known. Empty Atom feeds do\nnot overwrite an earlier detection.\n<code>use_credentials<\/code> defaults to false. When true, fetches use host-matched OAuth2\nconsumer tokens registered for the selected import account. Tokens are not stored\nin feed settings. Development fetch behavior follows <code>m_site:environment\/1<\/code>.\nNative import settings are <code>import_depth<\/code> (0–10, default 1), <code>is_subscribe<\/code> and\n<code>is_subscribe_connections<\/code> (both default false). The subscription flags apply\nthrough <code>mod_websub<\/code> to non-authoritative Zotonic resource imports.\nInternal lease tokens and saved user groups are omitted. History rows include <code>uri<\/code>,\n<code>rsc_id<\/code>, <code>kind<\/code>, <code>status<\/code> and processing times. Rows referring to resources the caller\ncannot view are filtered out; deleted resources have an undefined <code>rsc_id<\/code>.<\/p>\n<pre class=\"notranslate\"><code class=\"notranslate language-django\">{% for feed in m.import_feed.list %}\n    &lt;p&gt;{{ feed.title|escape }}: {{ feed.article_count }} articles&lt;\/p&gt;\n{% endfor %}\n<\/code><\/pre>\n<h2>Erlang API<\/h2>\n<p>Discovery, previews and writes are direct Erlang functions, not model POST paths.\nThe admin screens invoke them through <code>mod_import_feed<\/code> events.<\/p>\n<p><code>fetch_resource(Uri, Context)<\/code> implements the optional <code>rsc_import_fetch<\/code> handler.\nIt returns <code>undefined<\/code> for native Zotonic resources (which use the core importer)\nand unknown URIs, <code>{ok, Id}<\/code> on refresh, or an error. Known\narticles require module write permission and resource edit permission from the\ncaller, plus an enabled non-admin import account with permission to edit the article.\nIt refreshes one entry from the current feed, including for paused feeds, using\nsaved settings and WordPress enrichment. Missing entries return <code>feed_entry_not_found<\/code>.\nThe explicit refresh bypasses checksums but preserves deletion tombstones.<\/p>\n<ul><li><code>discover(Url, Context)<\/code> returns <code>{ok, Feeds}<\/code>, where each candidate has <code>url<\/code> and\n<code>title<\/code>, or <code>{error, Reason}<\/code>. It accepts a website URL or a feed URL.<\/li><li><code>preview(Url, Context)<\/code> returns <code>{ok, Feed}<\/code> with at most three example <code>entries<\/code>,\nusing WordPress enrichment. No resources are imported.<\/li><li><code>preview(Settings, Count, Context)<\/code> uses atom-keyed settings, such as a map from\n<code>get\/2<\/code>, and limits the result to at most three entries. With category settings,\nit also reports <code>target_category<\/code>, <code>dependent_count<\/code> and <code>attached_medium<\/code> per entry.<\/li><li><code>get(Id, Context)<\/code> returns <code>{ok, Feed}<\/code> or <code>{error, enoent}<\/code>.<\/li><li><code>list(Context)<\/code> returns feed maps. <code>items(Id, Offset, Context)<\/code> returns up to\n100 history rows, with a non-negative integer offset. Both return <code>[]<\/code> on ACL denial.<\/li><li><code>save(undefined, Input, Context)<\/code> creates a feed; <code>save(Id, Input, Context)<\/code> replaces\nits settings. Both return <code>{ok, Id}<\/code> or <code>{error, Reason}<\/code>. The feed URL is immutable.<\/li><li><code>pause(Id, Boolean, Context)<\/code> pauses or resumes a feed and returns <code>ok<\/code> or an error.<\/li><li><code>queue(Id, Reimport, Context)<\/code> marks a feed due and starts background processing.\nWith <code>Reimport = true<\/code>, it clears entry checksums, preserving source identities and\ndeletion records. It returns <code>ok<\/code> or an error, does not wait for imports to finish,\nand does not resume a paused feed.<\/li><\/ul>\n<p><code>can_select_user\/1<\/code> checks both feed access and the account-selection permission.\n<code>validate_user\/2<\/code> additionally rejects read-only contexts, missing accounts, resources\nwithout user credentials and disabled accounts; it returns <code>{ok, UserId}<\/code>,\n<code>{error, eacces}<\/code> or <code>{error, invalid_import_user}<\/code>.<\/p>\n<p>These administration functions check module permission; writes also reject read-only\ncontexts. <code>allowed\/1<\/code> reports module permission only. Exported <code>install\/1<\/code> and <code>decode\/1<\/code>\nare internal schema\/worker helpers and do not form an ACL-protected administration API.<\/p>\n<h2>Saving settings<\/h2>\n<p><code>save\/3<\/code> accepts a complete map with binary keys, matching the admin form. Keys are\n<code>url<\/code>, <code>title<\/code>, <code>category_id<\/code>, <code>content_group_id<\/code>, <code>guess_category<\/code>, <code>connections<\/code>,\n<code>media_mode<\/code>, <code>poll_minutes<\/code>, <code>is_authoritative<\/code>, <code>use_wordpress<\/code>, <code>is_paused<\/code> and\n<code>user_id<\/code>. The optional <code>user_id<\/code> selects an enabled account; selecting another account\nrequires administrator status or <code>use<\/code> permission for <code>mod_admin_identity<\/code>. Omission\non creation uses the current user; omission on update preserves the saved identity\nand groups. Submitted group memberships are never accepted.\nCategory and content-group values can be resource IDs or names. Connections are a\nbinary with one <code>predicate=resource<\/code> per line, or a list of such binaries from the\nconnection picker. At most 50 connections are accepted and duplicates are removed. Media mode is <code>dependent<\/code>, <code>attached<\/code>\nor <code>none<\/code>, as a binary. Polling accepts 5 to 10080 minutes.<\/p>\n<p>Omitted booleans are false in this API, including <code>use_wordpress<\/code>; the admin form\nexplicitly enables WordPress enrichment. The defaults for category, media mode and\npolling are Article, dependent media and 60 minutes. Changing the selected user records that account&#39;s groups for future imports. Ordinary\nsettings saves preserve them. Saving makes the feed due immediately unless paused.<\/p>\n<pre class=\"notranslate\"><code class=\"notranslate language-erlang\">{ok, FeedId} = m_import_feed:save(undefined, #{\n    &lt;&lt;&quot;url&quot;&gt;&gt; =&gt; &lt;&lt;&quot;https:\/\/example.org\/feed\/&quot;&gt;&gt;,\n    &lt;&lt;&quot;title&quot;&gt;&gt; =&gt; &lt;&lt;&quot;Example articles&quot;&gt;&gt;,\n    &lt;&lt;&quot;category_id&quot;&gt;&gt; =&gt; &lt;&lt;&quot;article&quot;&gt;&gt;,\n    &lt;&lt;&quot;use_wordpress&quot;&gt;&gt; =&gt; true,\n    &lt;&lt;&quot;is_paused&quot;&gt;&gt; =&gt; true\n}, Context).\n<\/code><\/pre>\n<p>Common errors include <code>eacces<\/code>, <code>invalid_settings<\/code>, <code>feed_url_is_immutable<\/code>,\n<code>save_failed_or_duplicate_url<\/code> and <code>enoent<\/code>. Discovery and previews also propagate\nfetch and parse errors. See <code>mod_import_feed<\/code> for import semantics and notifications.<\/p>","slug":"import_feed","is_protected":false,"visible_for":0,"tz":"UTC","language":["en"],"doc_source_hash":"16e8da72f0aaedd132a29e093146b8301b9cf65560002fade1b3e0bd5537ec24","is_featured":false,"content_group_id":{"id":2551,"is_a":["meta","content_group"],"name":"content_group_imported_docs","title":"Imported documentation","uri":"https:\/\/zotonic.com\/id\/content_group_imported_docs"},"category_id":{"id":322,"is_a":["meta","category"],"name":"model","title":"Models","uri":"https:\/\/test.zotonic.com\/id\/322"},"doc_source_path":"apps\/zotonic_mod_import_feed\/src\/models\/m_import_feed.erl","publication_start":"2026-10-13T15:15:56Z","github_url":"https:\/\/github.com\/zotonic\/zotonic\/blob\/master\/apps\/zotonic_mod_import_feed\/src\/models\/m_import_feed.erl","pivot_location_lng":null,"doc_source_kind":"model","name":"doc_model_model_import_feed","is_unfindable":false,"is_published":true,"pivot_geocode":null,"created":"2026-10-13T15:15:56Z","uri":null,"doc_status":"current","is_dependent":false,"erlang_app":"zotonic_mod_import_feed","publication_end":"9999-06-01T00:00:00Z","modifier_id":{"id":1,"is_a":["person"],"name":"administrator","title":"Site Administrator","uri":"https:\/\/zotonic.com\/id\/1"},"privacy":0,"doc_source_commit":"92d27433689eef55504265c6dd5486c65dd73483\n","erlang_module":"m_import_feed","creator_id":{"id":1,"is_a":["person"],"name":"administrator","title":"Site Administrator","uri":"https:\/\/zotonic.com\/id\/1"},"modified":"2026-10-13T15:40:30Z","title_slug":"import_feed"},"uri":"https:\/\/zotonic.com\/id\/3087","uri_template":"https:\/\/zotonic.com\/id\/:id","websub":{"hub":"https:\/\/zotonic.com\/.zotonic\/websub","topic":"https:\/\/zotonic.com\/.zotonic\/websub\/topic\/3087"}},"status":"ok"}