├── LICENSE.md
├── composer.json
├── docs
├── .gitignore
├── .vitepress
│ ├── config.mjs
│ ├── head.js
│ ├── sidebar.json
│ └── utils.js
├── content-mapping
│ ├── element-types.md
│ └── field-types.md
├── developers
│ ├── data-types.md
│ ├── element-types.md
│ ├── events.md
│ └── field-types.md
├── feature-tour
│ ├── creating-your-feed.md
│ ├── feed-overview.md
│ ├── field-mapping.md
│ ├── importing-your-content.md
│ ├── primary-element.md
│ ├── trigger-import-via-cron.md
│ └── using-in-your-templates.md
├── get-started
│ ├── configuration.md
│ └── installation-setup.md
├── guides
│ ├── importing-assets.md
│ ├── importing-commerce-products.md
│ ├── importing-commerce-variants.md
│ ├── importing-entries.md
│ ├── importing-into-matrix.md
│ ├── migrating-from-expressionengine.md
│ └── migrating-from-wordpress.md
├── index.md
├── package-lock.json
├── package.json
├── public
│ └── logo.svg
├── screenshots
│ ├── category.png
│ ├── commerce.png
│ ├── entry.png
│ ├── feedme-guide-asset-field-mapping.png
│ ├── feedme-guide-asset-finish.png
│ ├── feedme-guide-commerce-product-setup-mapping-multi.png
│ ├── feedme-guide-commerce-product-setup-mapping-single.png
│ ├── feedme-guide-commerce-product-setup-single.png
│ ├── feedme-guide-commerce-product-success-single.png
│ ├── feedme-guide-finish.png
│ ├── feedme-guide-mapping.png
│ ├── feedme-guide-setup.png
│ ├── feedme-logs.png
│ ├── feedme-mapping.png
│ ├── feedme-matrix-guide-finish1.png
│ ├── feedme-matrix-guide-finish2.png
│ ├── feedme-matrix-guide-mapping.png
│ ├── feedme-matrix-guide-setup.png
│ ├── feedme-matrix-guide-start.png
│ ├── feedme-overview.png
│ ├── feedme-setup-1.png
│ ├── feedme-setup-2.png
│ ├── feedme-setup-3.png
│ ├── feedme-setup-4.png
│ ├── feedme-setup-5.png
│ ├── feedme-setup-6.png
│ ├── feedme-setup-7.png
│ ├── feedme-setup-8.png
│ ├── feedme-setup-9.png
│ ├── feedme-setup.png
│ ├── feedme-start.png
│ ├── feedme-task.png
│ ├── main.png
│ └── user.png
└── troubleshooting.md
└── src
├── Plugin.php
├── base
├── DataType.php
├── DataTypeInterface.php
├── Element.php
├── ElementInterface.php
├── Field.php
├── FieldInterface.php
└── PluginTrait.php
├── console
└── controllers
│ ├── FeedsController.php
│ └── LogsController.php
├── controllers
├── BaseController.php
├── FeedsController.php
└── LogsController.php
├── datatypes
├── Atom.php
├── Csv.php
├── DataBatcher.php
├── GoogleSheet.php
├── Json.php
├── MissingDataType.php
├── Rss.php
└── Xml.php
├── elements
├── Asset.php
├── CalenderEvent.php
├── Category.php
├── CommerceProduct.php
├── DigitalProduct.php
├── Entry.php
├── GlobalSet.php
├── Tag.php
└── User.php
├── errors
└── FeedException.php
├── events
├── AssetFilenameEvent.php
├── ElementEvent.php
├── FeedDataEvent.php
├── FeedEvent.php
├── FeedProcessEvent.php
├── FieldEvent.php
├── RegisterFeedMeDataTypesEvent.php
├── RegisterFeedMeElementsEvent.php
└── RegisterFeedMeFieldsEvent.php
├── fields
├── Assets.php
├── CalendarEvents.php
├── Categories.php
├── Checkboxes.php
├── CommerceProducts.php
├── CommerceVariants.php
├── Country.php
├── Date.php
├── DefaultField.php
├── DigitalProducts.php
├── Dropdown.php
├── Entries.php
├── EntriesSubset.php
├── GoogleMaps.php
├── Icon.php
├── Lightswitch.php
├── Linkit.php
├── Matrix.php
├── MissingField.php
├── Money.php
├── MultiSelect.php
├── Number.php
├── RadioButtons.php
├── SimpleMap.php
├── SmartMap.php
├── SuperTable.php
├── Table.php
├── Tags.php
├── Time.php
├── TypedLink.php
└── Users.php
├── helpers
├── AssetHelper.php
├── BaseHelper.php
├── DataHelper.php
├── DateHelper.php
├── DuplicateHelper.php
└── FieldHelper.php
├── icon-mask.svg
├── icon.svg
├── migrations
├── Install.php
├── m230123_152413_set_empty_values.php
├── m240523_145259_field_mapping_column_change.php
└── m240611_134740_create_logs_table.php
├── models
├── ElementGroup.php
├── FeedModel.php
├── GetHelp.php
└── Settings.php
├── queue
└── jobs
│ └── FeedImport.php
├── records
└── FeedRecord.php
├── services
├── DataTypes.php
├── Elements.php
├── Feeds.php
├── Fields.php
├── Logs.php
├── Process.php
└── Service.php
├── templates
├── _includes
│ ├── elements
│ │ ├── assets
│ │ │ ├── column.html
│ │ │ ├── groups.html
│ │ │ └── map.html
│ │ ├── calendar-events
│ │ │ ├── column.html
│ │ │ ├── groups.html
│ │ │ └── map.html
│ │ ├── categories
│ │ │ ├── column.html
│ │ │ ├── groups.html
│ │ │ └── map.html
│ │ ├── commerce-products
│ │ │ ├── column.html
│ │ │ ├── groups.html
│ │ │ └── map.html
│ │ ├── digital-products
│ │ │ ├── column.html
│ │ │ ├── groups.html
│ │ │ └── map.html
│ │ ├── entries
│ │ │ ├── column.html
│ │ │ ├── groups.html
│ │ │ └── map.html
│ │ ├── global-sets
│ │ │ ├── column.html
│ │ │ ├── groups.html
│ │ │ └── map.html
│ │ ├── tag
│ │ │ ├── column.html
│ │ │ ├── groups.html
│ │ │ └── map.html
│ │ └── user
│ │ │ ├── column.html
│ │ │ ├── groups.html
│ │ │ └── map.html
│ └── fields
│ │ ├── _base.html
│ │ ├── assets-create.html
│ │ ├── assets-folder.html
│ │ ├── assets.html
│ │ ├── calendar-events.html
│ │ ├── categories.html
│ │ ├── commerce_products.html
│ │ ├── commerce_variants.html
│ │ ├── country.html
│ │ ├── date.html
│ │ ├── default.html
│ │ ├── digital-products.html
│ │ ├── element.html
│ │ ├── entries.html
│ │ ├── google-maps.html
│ │ ├── icon.html
│ │ ├── lightswitch.html
│ │ ├── linkit.html
│ │ ├── matrix.html
│ │ ├── money.html
│ │ ├── option-select.html
│ │ ├── simple-map.html
│ │ ├── smart-map.html
│ │ ├── super-table.html
│ │ ├── table.html
│ │ ├── tags.html
│ │ ├── time.html
│ │ ├── typed-link.html
│ │ ├── userGroups.html
│ │ └── users.html
├── _layouts
│ ├── index.html
│ └── settings.html
├── _macros
│ └── index.html
├── _svg
│ └── verbb-logo.svg
├── feeds
│ ├── _direct.html
│ ├── _edit.html
│ ├── _element.html
│ ├── _map.html
│ ├── _status.html
│ └── index.html
├── index.html
├── logs
│ └── index.html
├── settings
│ ├── general.html
│ └── index.html
├── utilities
│ └── index.html
└── welcome
│ └── index.html
└── web
├── assets
└── feedme
│ ├── FeedMeAsset.php
│ ├── dist
│ ├── FeedMe.js
│ ├── FeedMe.js.map
│ ├── css
│ │ ├── FeedMe.css
│ │ └── FeedMe.css.map
│ ├── fonts
│ │ ├── fa-regular-400.eot
│ │ ├── fa-regular-400.svg
│ │ ├── fa-regular-400.ttf
│ │ ├── fa-regular-400.woff
│ │ ├── fa-regular-400.woff2
│ │ ├── fa-solid-900.eot
│ │ ├── fa-solid-900.svg
│ │ ├── fa-solid-900.ttf
│ │ ├── fa-solid-900.woff
│ │ └── fa-solid-900.woff2
│ └── img
│ │ ├── icon-error.svg
│ │ ├── icon-info.svg
│ │ └── icon-success.svg
│ ├── src
│ ├── FeedMe.js
│ ├── fonts
│ │ ├── fa-regular-400.eot
│ │ ├── fa-regular-400.svg
│ │ ├── fa-regular-400.ttf
│ │ ├── fa-regular-400.woff
│ │ ├── fa-regular-400.woff2
│ │ ├── fa-solid-900.eot
│ │ ├── fa-solid-900.svg
│ │ ├── fa-solid-900.ttf
│ │ ├── fa-solid-900.woff
│ │ └── fa-solid-900.woff2
│ ├── img
│ │ ├── icon-error.svg
│ │ ├── icon-info.svg
│ │ └── icon-success.svg
│ ├── js
│ │ └── feed-me.js
│ ├── lib
│ │ └── selectize.js
│ └── scss
│ │ ├── _font-awesome.scss
│ │ ├── feed-me.css
│ │ ├── feed-me.css.map
│ │ └── feed-me.scss
│ └── webpack.config.js
└── twig
├── Extension.php
└── variables
└── FeedMeVariable.php
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright © Pixel & Tonic
2 |
3 | Permission is hereby granted to any person obtaining a copy of this software
4 | (the “Software”) to use, copy, modify, merge, publish and/or distribute copies
5 | of the Software, and to permit persons to whom the Software is furnished to do
6 | so, subject to the following conditions:
7 |
8 | 1. **Don’t plagiarize.** The above copyright notice and this license shall be
9 | included in all copies or substantial portions of the Software.
10 |
11 | 2. **Don’t use the same license on more than one project.** Each licensed copy
12 | of the Software shall be actively installed in no more than one production
13 | environment at a time.
14 |
15 | 3. **Don’t mess with the licensing features.** Software features related to
16 | licensing shall not be altered or circumvented in any way, including (but
17 | not limited to) license validation, payment prompts, feature restrictions,
18 | and update eligibility.
19 |
20 | 4. **Pay up.** Payment shall be made immediately upon receipt of any notice,
21 | prompt, reminder, or other message indicating that a payment is owed.
22 |
23 | 5. **Follow the law.** All use of the Software shall not violate any applicable
24 | law or regulation, nor infringe the rights of any other person or entity.
25 |
26 | Failure to comply with the foregoing conditions will automatically and
27 | immediately result in termination of the permission granted hereby. This
28 | license does not include any right to receive updates to the Software or
29 | technical support. Licensees bear all risk related to the quality and
30 | performance of the Software and any modifications made or obtained to it,
31 | including liability for actual and consequential harm, such as loss or
32 | corruption of data, and any necessary service, repair, or correction.
33 |
34 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
38 | LIABILITY, INCLUDING SPECIAL, INCIDENTAL AND CONSEQUENTIAL DAMAGES, WHETHER IN
39 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
40 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "craftcms/feed-me",
3 | "description": "Import content from XML, RSS, CSV or JSON feeds into entries, categories, Craft Commerce products, and more.",
4 | "type": "craft-plugin",
5 | "keywords": [
6 | "craft",
7 | "cms",
8 | "craftcms",
9 | "feed me"
10 | ],
11 | "license": "proprietary",
12 | "authors": [
13 | {
14 | "name": "Pixel & Tonic",
15 | "homepage": "https://pixelandtonic.com/"
16 | },
17 | {
18 | "name": "Verbb",
19 | "homepage": "https://verbb.io"
20 | }
21 | ],
22 | "support": {
23 | "email": "support@craftcms.com",
24 | "issues": "https://github.com/craftcms/feed-me/issues?state=open",
25 | "source": "https://github.com/craftcms/feed-me",
26 | "docs": "https://docs.craftcms.com/feed-me/v6/",
27 | "rss": "https://github.com/craftcms/feed-me/commits/master.atom"
28 | },
29 | "minimum-stability": "dev",
30 | "prefer-stable": true,
31 | "require": {
32 | "php": "^8.2",
33 | "craftcms/cms": "^5.6.0",
34 | "cakephp/utility": "^5.0.0",
35 | "jakeasmith/http_build_url": "^1.0",
36 | "nesbot/carbon": "^2.10|^3.0.0",
37 | "league/csv": "^9.0",
38 | "seld/jsonlint": "^1.7"
39 | },
40 | "require-dev": {
41 | "craftcms/ecs": "dev-main",
42 | "craftcms/phpstan": "dev-main",
43 | "codeception/codeception": "^5.0.11",
44 | "codeception/module-asserts": "^3.0.0"
45 | },
46 | "autoload": {
47 | "psr-4": {
48 | "craft\\feedme\\": "src/"
49 | }
50 | },
51 | "autoload-dev": {
52 | "psr-4": {
53 | "craft\\feedme\\tests\\": "tests/"
54 | }
55 | },
56 | "extra": {
57 | "name": "Feed Me",
58 | "handle": "feed-me",
59 | "documentationUrl": "https://docs.craftcms.com/feed-me/v4/"
60 | },
61 | "scripts": {
62 | "check-cs": "ecs check --ansi",
63 | "fix-cs": "ecs check --ansi --fix",
64 | "phpstan": "phpstan --memory-limit=1G"
65 | },
66 | "config": {
67 | "platform": {
68 | "php": "8.2"
69 | },
70 | "allow-plugins": {
71 | "yiisoft/yii2-composer": true,
72 | "craftcms/plugin-installer": true
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .vitepress/dist
3 | .vitepress/cache
4 |
--------------------------------------------------------------------------------
/docs/.vitepress/config.mjs:
--------------------------------------------------------------------------------
1 | import {defineConfig} from 'vitepress';
2 | import markdownDefList from 'markdown-it-deflist';
3 |
4 | import sidebarContent from './sidebar.json';
5 | import headContent from './head.js';
6 |
7 | import {renderInlineCode} from './utils.js';
8 |
9 | // https://vitepress.dev/reference/site-config
10 | export default defineConfig({
11 | // App
12 | base: '/feed-me/v6/',
13 | ignoreDeadLinks: true,
14 |
15 | // Documents
16 | head: headContent,
17 | title: 'Feed Me 6.x',
18 | description: 'Documentation for the official Craft CMS Feed Me plugin',
19 |
20 | // Theme
21 | // https://vitepress.dev/reference/default-theme-config
22 | themeConfig: {
23 | logo: '/logo.svg',
24 | search: {
25 | provider: 'local',
26 | },
27 | nav: [
28 | {
29 | text: 'More',
30 | items: [
31 | {text: 'Documentation', link: 'https://craftcms.com/docs/5.x'},
32 | {text: 'Knowledge Base', link: 'https://craftcms.com/knowledge-base'},
33 | {
34 | text: 'Craft Class Reference',
35 | link: 'https://docs.craftcms.com/api/v5',
36 | },
37 | {text: 'Craftnet API', link: 'https://docs.api.craftcms.com/'},
38 | ],
39 | },
40 | ],
41 | socialLinks: [
42 | {icon: 'github', link: 'https://github.com/craftcms/feed-me'},
43 | ],
44 | sidebar: sidebarContent,
45 | },
46 |
47 | // Output Filtering
48 | markdown: {
49 | config(md) {
50 | // Add HTML5 definition list syntax support:
51 | md.use(markdownDefList);
52 |
53 | // Disable interpolation for inline code:
54 | md.renderer.rules.code_inline = renderInlineCode;
55 | },
56 | },
57 |
58 | // Hooks
59 | async transformPageData(pageData, {siteConfig}) {
60 | // console.log(`Handling: ${pageData.title} (${pageData.relativePath})`);
61 | },
62 |
63 | // Sitemap
64 | sitemap: {
65 | hostname: 'https://docs.craftcms.com/feed-me/v6',
66 | },
67 |
68 | // Builds
69 | buildConcurrency: 2,
70 | });
71 |
--------------------------------------------------------------------------------
/docs/.vitepress/head.js:
--------------------------------------------------------------------------------
1 | export default [
2 | // Branding, etc.
3 | [
4 | 'link',
5 | {
6 | rel: 'icon',
7 | href: 'https://docs.craftcms.com/siteicons/favicon-16x16.png',
8 | },
9 | ],
10 | [
11 | 'link',
12 | {
13 | rel: 'apple-touch-icon',
14 | sizes: '180x180',
15 | href: 'https://docs.craftcms.com/siteicons/apple-touch-icon.png',
16 | },
17 | ],
18 | [
19 | 'link',
20 | {
21 | rel: 'icon',
22 | type: 'image/png',
23 | sizes: '32x32',
24 | href: 'https://docs.craftcms.com/siteicons/favicon-32x32.png',
25 | },
26 | ],
27 | [
28 | 'link',
29 | {
30 | rel: 'icon',
31 | type: 'image/png',
32 | sizes: '16x16',
33 | href: 'https://docs.craftcms.com/siteicons/favicon-16x16.png',
34 | },
35 | ],
36 | [
37 | 'link',
38 | {
39 | rel: 'mask-icon',
40 | href: 'https://docs.craftcms.com/siteicons/safari-pinned-tab.svg',
41 | color: '#e5422b',
42 | },
43 | ],
44 | [
45 | 'meta',
46 | {
47 | name: 'msapplication-TileColor',
48 | content: '#f1f5fd',
49 | },
50 | ],
51 | [
52 | 'meta',
53 | {
54 | name: 'msapplication-config',
55 | content: 'https://docs.craftcms.com/browserconfig.xml',
56 | },
57 | ],
58 | [
59 | 'meta',
60 | {
61 | name: 'theme-color',
62 | content: '#f1f5fd',
63 | },
64 | ],
65 | ];
66 |
--------------------------------------------------------------------------------
/docs/.vitepress/sidebar.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "text": "Get Started",
4 | "items": [
5 | {"text": "Introduction", "link": "/"},
6 | {"text": "Installation", "link": "get-started/installation-setup"},
7 | {"text": "Configuration", "link": "get-started/configuration"}
8 | ]
9 | },
10 | {
11 | "text": "Feature Tour",
12 | "items": [
13 | {"text": "Feeds", "link": "feature-tour/feed-overview"},
14 | {"text": "Creating a Feed", "link": "feature-tour/creating-your-feed"},
15 | {"text": "Primary Element", "link": "feature-tour/primary-element"},
16 | {"text": "Field Mapping", "link": "feature-tour/field-mapping"},
17 | {
18 | "text": "Importing Content",
19 | "link": "feature-tour/importing-your-content"
20 | },
21 | {
22 | "text": "Scheduled Imports",
23 | "link": "feature-tour/trigger-import-via-cron"
24 | },
25 | {"text": "Templating", "link": "feature-tour/using-in-your-templates"}
26 | ]
27 | },
28 | {
29 | "text": "Content Mapping",
30 | "items": [
31 | {"text": "Element Types", "link": "content-mapping/element-types"},
32 | {"text": "Field Types", "link": "content-mapping/field-types"}
33 | ]
34 | },
35 | {
36 | "text": "Developers",
37 | "items": [
38 | {"text": "Field Types", "link": "developers/field-types"},
39 | {"text": "Element Types", "link": "developers/element-types"},
40 | {"text": "Data Types", "link": "developers/data-types"},
41 | {"text": "Events", "link": "developers/events"}
42 | ]
43 | },
44 | {
45 | "text": "Importing Guides",
46 | "items": [
47 | {"text": "Assets", "link": "guides/importing-assets"},
48 | {"text": "Entries", "link": "guides/importing-entries"},
49 | {
50 | "text": "Craft Commerce",
51 | "link": "guides/importing-commerce-products"
52 | },
53 | {
54 | "text": "Matrix Fields",
55 | "link": "guides/importing-into-matrix"
56 | },
57 | {
58 | "text": "Expression Engine",
59 | "link": "guides/migrating-from-expressionengine"
60 | },
61 | {"text": "WordPress", "link": "guides/migrating-from-wordpress"}
62 | ]
63 | },
64 | {
65 | "text": "Troubleshooting",
66 | "items": [{"text": "Troubleshooting", "link": "troubleshooting"}]
67 | }
68 | ]
69 |
--------------------------------------------------------------------------------
/docs/.vitepress/utils.js:
--------------------------------------------------------------------------------
1 | import {escapeHtml} from 'markdown-it/lib/common/utils';
2 |
3 | /**
4 | * Wraps all inline code snippets in `v-pre` to prevent interpolation of Twig.
5 | */
6 | function renderInlineCode(tokens, idx, options, env, renderer) {
7 | var token = tokens[idx];
8 |
9 | return `${escapeHtml(
10 | tokens[idx].content
11 | )}
`;
12 | }
13 |
14 | export {renderInlineCode};
15 |
--------------------------------------------------------------------------------
/docs/content-mapping/element-types.md:
--------------------------------------------------------------------------------
1 | # Element Types
2 |
3 | Selecting which element type your import targets is the first step in using Feed Me. As each element type is different, the presented [mapping](../feature-tour/field-mapping.md) options will be unique.
4 |
5 | ## Built-in Importers
6 |
7 | The following element types are supported by Feed Me, out of the box:
8 |
9 | - **[Core element types](https://craftcms.com/docs/5.x/system/elements.html):** Assets, categories, entries, tags, users, and addresses (via an address custom field);
10 | - **[Craft Commerce](https://plugins.craftcms.com/commerce):** Products and variants;
11 | - **[Digital Products](https://plugins.craftcms.com/digital-products):** Digital products;
12 | - **[Calendar by Solspace](https://plugins.craftcms.com/calendar):** Events;
13 | - **[Verbb Comments](https://plugins.craftcms.com/comments):** Comments;
14 |
15 | ::: tip
16 | Plugins can add support for their own [custom element types](../developers/element-types.md), as well.
17 | :::
18 |
--------------------------------------------------------------------------------
/docs/developers/data-types.md:
--------------------------------------------------------------------------------
1 | # Data Types
2 |
3 | ### The `registerFeedMeDataTypes` event
4 | Plugins can register their own data types.
5 |
6 | ```php
7 | use craft\feedme\events\RegisterFeedMeDataTypesEvent;
8 | use craft\feedme\services\DataTypes;
9 | use yii\base\Event;
10 |
11 | Event::on(DataTypes::class, DataTypes::EVENT_REGISTER_FEED_ME_DATA_TYPES, function(RegisterFeedMeDataTypesEvent $e) {
12 | $e->dataTypes[] = MyDataType::class;
13 | });
14 | ```
15 |
--------------------------------------------------------------------------------
/docs/developers/element-types.md:
--------------------------------------------------------------------------------
1 | # Element Types
2 |
3 | ### The `registerFeedMeElements` event
4 | Plugins can register their own elements.
5 |
6 | ```php
7 | use craft\feedme\events\RegisterFeedMeElementsEvent;
8 | use craft\feedme\services\Elements;
9 | use yii\base\Event;
10 |
11 | Event::on(Elements::class, Elements::EVENT_REGISTER_FEED_ME_ELEMENTS, function(RegisterFeedMeElementsEvent $e) {
12 | $e->elements[] = MyElement::class;
13 | });
14 | ```
15 |
16 | ### The `beforeParseAttribute` event
17 | Plugins can get notified before a element's attribute has been parsed.
18 |
19 | ```php
20 | use craft\feedme\base\Element;
21 | use craft\feedme\events\ElementEvent;
22 | use yii\base\Event;
23 |
24 | Event::on(Element::class, Element::EVENT_BEFORE_PARSE_ATTRIBUTE, function(ElementEvent $e) {
25 |
26 | });
27 | ```
28 |
29 | ### The `parseAttribute` event
30 | Plugins can get notified after a element's attribute has been parsed.
31 |
32 | ```php
33 | use craft\feedme\base\Element;
34 | use craft\feedme\events\ElementEvent;
35 | use yii\base\Event;
36 |
37 | Event::on(Element::class, Element::EVENT_AFTER_PARSE_ATTRIBUTE, function(ElementEvent $e) {
38 | $parsedValue = $e->parsedValue;
39 | });
40 | ```
41 |
--------------------------------------------------------------------------------
/docs/feature-tour/feed-overview.md:
--------------------------------------------------------------------------------
1 | # Feed Overview
2 |
3 | Go to the main Feed Me section via your CP sidebar menu. You'll be presented with a table listing of your saved feeds, or none if you haven't set any up yet.
4 |
5 | This overview shows the following:
6 |
7 | - **Name** — Name your feed something useful so you'll remember what it does.
8 | - **Feed URL** — The URL (or filesystem path) to your feed.
9 | - **Type** — The data type you're importing.
10 | - **Element Type** — The element type you are importing into.
11 | - Depending on the element type chosen. Entries will show **Section/Entry Type**, Categories will show **Group**, etc.
12 | - **Target Site** — Which site content should be imported into. Each import targets a single site.
13 | - **Strategy** — One or more descriptos of how Feed Me reconciles entries in initial and subsequent runs. See [import strategy](creating-your-feed.md#import-strategy) for more information.
14 | - **Process** — Quickly queue a background import job, without adjusting settings.
15 |
16 | The final column displays a few actions:
17 |
18 | - **Settings** (icon) — [See below](#settings-pane).
19 | - **Move** (icon) — Drag and drop the row to organize your feeds.
20 | - **Delete (icon)** — Delete the feed.
21 |
22 | ### Settings Pane
23 |
24 | Clicking on the settings “cog” icon will expand additional settings for a feed:
25 |
26 | - **Debug** — Opens in a new window and runs the [Debug action](../troubleshooting.md#debugging).
27 | - **Feed Status** — Takes you to an overview screen showing the process of your feed (if it’s currently running).
28 | - **Duplicate Feed** — Duplicates the feed into a new feed, preserving all its settings.
29 | - **Direct Feed URL** — Can be used in your [Cron job setup](trigger-import-via-cron.md) to directly trigger the job.
30 |
31 | ### Creating and Editing Feeds
32 |
33 | [Create a new feed](creating-your-feed.md) by pressing the red **+ New Feed** button. To edit an existing feed’s settings, click its name in the first column.
34 |
--------------------------------------------------------------------------------
/docs/feature-tour/importing-your-content.md:
--------------------------------------------------------------------------------
1 | # Importing your Content
2 |
3 | Once you've completed mapping your fields, you can import your content. You'll see an overview screen with a loading bar showing the progress of your import. If any errors occur, you'll be notified here.
4 |
5 | 
6 |
7 | You can navigate away from this page once started, as your import is run as a Craft queue jobs. You can see the status of your feed at any time through the queue job sidebar notification.
8 |
--------------------------------------------------------------------------------
/docs/feature-tour/primary-element.md:
--------------------------------------------------------------------------------
1 | # Primary Element
2 |
3 | The primary element can be confusing at first, but its vitally important to ensure Feed Me can hone in on the content in your feed correctly.
4 |
5 | Take the following example:
6 |
7 | ::: code-group
8 | ```xml
9 |
10 |
11 |
12 | -
13 |
My Title
14 | my-title
15 |
16 |
17 | -
18 |
Another Title
19 | another-title
20 |
21 |
22 |
23 | ```
24 |
25 | ```json
26 | {
27 | "channel": {
28 | "item": [
29 | {
30 | "title": "My Title",
31 | "slug": "my-title"
32 | },
33 | {
34 | "title": "Another Title",
35 | "slug": "another-title"
36 | }
37 | ]
38 | }
39 | }
40 | ```
41 | :::
42 |
43 | Your Primary Element would be `item`. This is the node that's repeatable, and you can usually pick it as it'll be the node thats one level above the content you want to import (`title` and `slug` in this case). In the JSON example, you'll see its a plain array, but the same applies.
44 |
45 | As a helper, Feed Me will show you how many elements on each node, which will give you a clue as to which node you want to select as the primary element. As each feed can be vastly different, this step seeks to normalise them for Feed Me to effectively process.
46 |
47 | ## Pagination URL
48 |
49 | Some feeds paginate their content due to their sheer size. This is also a good idea for performance, where instead of a massive feed to update 600 items, there are 6 feeds with 100 items each. In this instance, your feed should contain the full URL to the next collection of items for Feed Me to fetch.
50 |
51 | Use this option to select the node in your feed that contains the full URL to the next collection of items. Feed Me will automatically spawn a new queue job to process this new set of data after the first page has finished.
52 |
53 | ::: warning
54 | Your pagination URL can’t be nested within an array or numerically-indexed key in your feed.
55 | :::
56 |
57 | Press _Save & Continue_ to be taken to the [Field Mapping](field-mapping.md) screen, or press _Save_ to continue editing this screen.
58 |
--------------------------------------------------------------------------------
/docs/feature-tour/trigger-import-via-cron.md:
--------------------------------------------------------------------------------
1 | # Trigger Import via Cron
2 |
3 | :::tip
4 | Triggering an import via Cron still uses Craft's [queue system](https://craftcms.com/docs/5.x/system/queue.html).
5 | :::
6 |
7 | Once your feed is configured properly, you can trigger the feed processing directly using a special URL. Find this URL by copying the **Direct Feed Link** from the [Feed Overview](feed-overview.md) screen. You'll receive a URL that looks something like this:
8 |
9 | ```
10 | https://my-project.ddev.site/index.php?action=feed-me/feeds/run-task&direct=1&feedId=1&passkey=FwafY5kg3c
11 | ```
12 |
13 | #### Parameters
14 |
15 | - `direct` (required) - Must be set to 1 or true. Tells Feed Me this is a externally-triggered queue job.
16 | - `feedId` (required) - The ID of the feed you wish to process.
17 | - `passkey` (required) - A unique, generated identifier for this feed. Ensures not just anyone can trigger the import.
18 | - `url` (optional) - If your feed URL changes (or is split/parameterized in some way, like using the current date), you can override it here. Ensure the structure of the feed matches your field mappings.
19 |
20 | ::: warning
21 | Feed IDs and passkeys may not be the same, across environments. Always refer to the control panel for the complete URL.
22 | :::
23 |
24 | #### Setup
25 |
26 | To setup this feed to run via cron, use one of the following commands - replacing the URL with the one for your feed. Which command you use will depend on your server capabilities, but `wget` is the most common.
27 |
28 | ::: code-group
29 | ```bash wget
30 | /usr/bin/wget -O - -q -t 1 "https://my-project.ddev.site/index.php?action=feed-me/feeds/run-task&direct=1&feedId=1&passkey=FwafY5kg3c"
31 | ```
32 | ```bash cURL
33 | curl --silent --compressed "https://my-project.ddev.site/index.php?action=feed-me/feeds/run-task&direct=1&feedId=1&passkey=FwafY5kg3c"
34 | ```
35 | :::
36 |
37 | ### Console command
38 |
39 | You can also trigger your feed via Craft’s CLI by passing a comma-separated list of feed IDs:
40 |
41 | ```bash
42 | php craft feed-me/feeds/queue 1
43 |
44 | php craft feed-me/feeds/queue 1,2,3
45 | ```
46 |
47 | You can also use `limit` and `offset` parameters:
48 |
49 | ```bash
50 | php craft feed-me/feeds/queue 1 --limit=1
51 |
52 | php craft feed-me/feeds/queue 1 --limit=1 --offset=1
53 |
54 | php craft feed-me/feeds/queue 1 --continue-on-error
55 | ```
56 |
57 | Use the `--all` flag to push _all_ your feeds into the queue. This parameter ignores `--limit` and `--offset` settings.
58 |
59 | ```bash
60 | php craft feed-me/feeds/queue --all
61 | ```
62 |
63 | ::: warning
64 | The `feed-me/feeds/queue` command only _queues_ import jobs. To actually execute those jobs, the queue must be running—by default, the queue is triggered when your site is loaded by a client, or a control panel user is active. Projects with many feeds may benefit from setting up a [daemonized runner](https://craftcms.com/docs/5.x/system/queue.html#daemon), or manually running the queue [on a schedule](https://craftcms.com/docs/5.x/system/queue.html#cron).
65 | :::
66 |
--------------------------------------------------------------------------------
/docs/feature-tour/using-in-your-templates.md:
--------------------------------------------------------------------------------
1 | # Using in your Templates
2 |
3 | While you can create a feed queue job to insert data as elements, there are times when you may prefer to capture feed data on-demand. You can easily do this in your Twig templates using Feed Me’s API.
4 |
5 | Feeds are cached for performance (default to 60 seconds), which can be set by a tag parameter, or in the plugin settings.
6 |
7 | ```twig{5}
8 | {% set params = {
9 | url: 'http://path.to/feed/',
10 | type: 'xml',
11 | element: 'item',
12 | cache: 60,
13 | } %}
14 |
15 | {% set feed = craft.feedme.feed(params) %}
16 |
17 | {% for node in feed %}
18 | {# Your template code goes here #}
19 | {% endfor %}
20 | ```
21 |
22 | ::: danger
23 | Do not issue requests to user-supplied URLs! If you must parameterize a feed URL, validate the incoming data, first.
24 | :::
25 |
26 | #### Parameters
27 |
28 | - `url` (string, required) - URL or path to the feed.
29 | - `type` (string, optional) - The type of feed you're fetching data from. Valid options are json or xml (defaults to xml).
30 | - `element` (string, optional) - Element to start feed from. Useful for deep feeds.
31 | - `cache` (bool or number, optional) - Whether or not to cache the request. If true, will use the default as set in the plugin settings, or if a number, will use that as its duration. Setting to false will disable cache completely.
32 |
33 | ### Example template code
34 |
35 | ```xml
36 |
37 |
38 |
39 | Monday
40 | -
41 |
Event 1
42 | All-day
43 |
44 |
45 |
46 |
47 | Tuesday
48 | -
49 |
Event 2
50 | Half-day
51 |
52 |
53 |
54 | ```
55 |
56 | With the above example XML, we would use the following Twig code to loop through each `entry` to extract its data.
57 |
58 | ```twig
59 | {% set params = {
60 | url: 'http://path.to/feed/',
61 | type: 'xml',
62 | element: 'entry',
63 | cache: 60,
64 | } %}
65 |
66 | {% set feed = craft.feedme.feed(params) %}
67 |
68 | {% for node in feed %}
69 | Title: {{ node.title }}
70 | Item: {{ node.item.title['@'] }}
71 | Item Format: {{ node.item.title['@format'] }}
72 | Type: {{ node.item.type }}
73 | {% endfor %}
74 |
75 | {# Producing the following output #}
76 | Title: Monday
77 | Item: Event 1
78 | Item Format: html
79 | Type: All-day
80 |
81 | Title: Tuesday
82 | Item: Event 2
83 | Item Format: html
84 | Type: Half-day
85 | ```
86 |
87 | :::tip
88 | There's a special case for XML-based feeds, which is illustrated above when attributes are present on a node. To retrieve the node value, use `['@']`, and to fetch the attribute value, use `['@attribute_name']`.
89 | :::
90 |
--------------------------------------------------------------------------------
/docs/get-started/installation-setup.md:
--------------------------------------------------------------------------------
1 | # Installation & Setup
2 | You can install Feed Me with the in-app [Plugin Store](https://plugins.craftcms.com), or via Composer.
3 |
4 | ### Requirements
5 |
6 | Feed Me 6.x requires Craft CMS 5.0.0 or newer.
7 |
8 | ### Craft Plugin Store
9 |
10 | To install **Feed Me**, navigate to the _Plugin Store_ section of your Craft control panel, search for `Feed Me`, and click the _Try_ button.
11 |
12 | ### Composer
13 |
14 | You can also add the package to your project using Composer.
15 |
16 | 1. Open your terminal and go to your Craft project:
17 |
18 | ```bash
19 | cd /path/to/project
20 | ```
21 |
22 | 2. Require the package with Composer:
23 |
24 | ```bash
25 | composer require craftcms/feed-me
26 | ```
27 |
28 | 3. Install the plugin:
29 |
30 | ```bash
31 | php craft plugin/install feed-me
32 | ```
33 |
--------------------------------------------------------------------------------
/docs/guides/importing-commerce-variants.md:
--------------------------------------------------------------------------------
1 | # Importing Commerce Variants
2 |
3 | ::: warning
4 | This guide is no longer maintained! [Commerce variants](https://craftcms.com/docs/commerce/5.x/system/products-variants.html) are imported as nested records of [products](importing-commerce-products.md).
5 | :::
--------------------------------------------------------------------------------
/docs/guides/migrating-from-expressionengine.md:
--------------------------------------------------------------------------------
1 | # Migrating from ExpressionEngine
2 |
3 | Migrating from ExpressionEngine to Craft is relatively straightforward in most cases, due to the similarities between systems. This guide will aim to provide a walkthrough on a generic migration process. Of course - just as each website is different, so too will each migration.
4 |
5 | This guide is also aimed at ExpressionEngine 2.x. We will not be offering a guide for ExpressionEngine 3.x.
6 |
7 | :::tip
8 | We are unable to offer any dedicated support for exporting out of ExpressionEngine. It is up to you how you get your data, and this guide is for reference only.
9 | :::
10 |
11 | ### Create export from ExpressionEngine
12 |
13 | The first step is to create a template in ExpressionEngine to export your content. You could create a template per-channel if you prefer, but for ease-of-use, we create a single file, passing in the desired channel as a query parameter.
14 |
15 | Copy and paste the contents of [this Gist](https://gist.github.com/engram-design/5fbe54ef0abb15e3ff6f667291098464) into a new template in ExpressionEngine. Lets call it `export.html`.
16 |
17 | :::tip
18 | Make sure you enable PHP execution on this template.
19 | :::
20 |
21 | Now, visit the following URL (for your ExpressionEngine site):
22 |
23 | ```
24 | http://my.ee.site/export?id=9
25 | ```
26 |
27 | With `id` being the ID of your channel. This template code will loop through all custom fields for this channel, grab the data from the contents table, and output as JSON. Either save the contents of this page as `*.json` file, or save the URL for use with Feed Me.
28 |
29 | ### Settings up Feed Me
30 | Follow the guide to [Importing Entries](importing-entries.md), using the URL or file from the previous step.
31 |
--------------------------------------------------------------------------------
/docs/guides/migrating-from-wordpress.md:
--------------------------------------------------------------------------------
1 | # Migrating from WordPress
2 |
3 | Migrating from WordPress to Craft can be challenging—but the bulk of the effort tends to be getting your content _out_ of WordPress (and various plugins’ content storage systems) and into a format that Feed Me understands.
4 |
5 | ::: tip
6 | Consider reviewing the [updated recommendations](https://craftcms.com/knowledge-base/for-wordpress-devs) for Craft 5.x, which makes use of our new [dedicated import tool](https://github.com/craftcms/wp-import).
7 |
8 | While we are happy to support Feed Me and the `wp-import` extension, we are unable to offer help with third-party WordPress export tools. This guide provides some general recommendations, but may not be a turn-key solution for every WordPress installation!
9 | :::
10 |
11 | ## Create export from WordPress
12 |
13 | ### Plugin Exporters
14 |
15 | The first step is to get your data out of WordPress. We’ll assume you’re using the free [WP All Export](https://wordpress.org/plugins/wp-all-export/) plugin. It'll produce an XML file of your content and supports all native fields, Advanced Custom Fields, WooCommerce, custom taxonomies, and custom post types.
16 |
17 | Use one or more of the tutorials to set up exports for the data you need. Keep in mind that you can import content in multiple stages via different feeds—you don't have to import (or export) _everything_ at once!
18 |
19 | ::: warning
20 | Make sure the WordPress `ID` is present for every record you export.
21 | :::
22 |
23 | ### WP JSON
24 |
25 | If you have a relatively vanilla WordPress installation, you may be able to use the [built-in REST API](https://learn.wordpress.org/tutorial/using-the-wordpress-rest-api/) to dynamically retrieve your site’s content as JSON.
26 |
27 | ### Feed Me Settings
28 |
29 | Follow the guide to [Importing Entries](importing-entries.md), using the URL or file from the previous step. You may need to run multiple imports for different WordPress resources, mapped to the corresponding Craft element types—like _media_ ([assets](importing-assets.md)), _users_, and _taxonomies_.
30 |
31 | ### How to Handle IDs
32 |
33 | When importing records from WordPress, they will receive new IDs. Create a custom field for legacy WordPress `ID`s and attach it to every import target (entry type, asset volume, etc.). When you set up your feed, map the WordPress ID to that custom field. This will help match up content if you need to re-import it again at a later date—and to connect things like posts and categories.
34 |
35 | To illustrate, the WordPress post with `ID` `123`…
36 |
37 | ```php{1}
38 | $post = get_post(123);
39 | get_the_title($post);
40 | // -> "Importing into Craft CMS"
41 | ```
42 |
43 | …will probably not end up with the same `id` in Craft…
44 |
45 | ```twig{3}
46 | {% set post = craft.entries()
47 | .section('posts')
48 | .id(123)
49 | .one() %}
50 |
51 | {{ post.title }}
52 | {# -> "How we use Advanced Custom Fields" 🚫 #}
53 | ```
54 |
55 | By assigning the post `ID` to a custom field, you can look them up using the corresponding query method for the custom field:
56 |
57 | ```twig{3}
58 | {% set post = craft.entries()
59 | .section('posts')
60 | .legacyWpId(123)
61 | .one() %}
62 |
63 | {{ post.title }}
64 | {# -> "Importing into Craft CMS" ✅ #}
65 | ```
66 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # About Feed Me
2 |
3 | Feed Me is a content importing and synchronization plugin for [Craft CMS](https://craftcms.com). You can set up one-time and repeatable imports to synchronize XML, RSS, ATOM, CSV or JSON feeds with your existing content types—entries, categories, Craft Commerce products (and variants), and more!
4 |
5 | ## Features
6 |
7 | - Import data from local or remote XML, RSS, ATOM, CSV, or JSON sources.
8 | - Built-in importers for [several element types](content-mapping/element-types.md), plus an extensible [importer API](developers/element-types.md).
9 | - Persistent feed configuration, allowing re-processing via the control panel, webhooks, or CLI (including CRON).
10 | - Sophisticated element-matching and field-mapping interface to get content where it needs to go.
11 | - Automatic duplicate reconciliation for repeated imports.
12 | - Pagination and background processing for large feeds.
13 | - Automatic database backups before each feed processing.
14 | - Troubleshoot feed processing issues with verbose, context-rich logs.
15 | - Use feed data directly in your Twig templates.
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@craftcms/feed-me-docs",
4 | "devDependencies": {
5 | "markdown-it": "^14.1.0",
6 | "markdown-it-deflist": "^2.0.3",
7 | "vitepress": "^1.2.3"
8 | },
9 | "scripts": {
10 | "docs:dev": "vitepress dev .",
11 | "docs:build": "vitepress build .",
12 | "docs:preview": "vitepress preview ."
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/docs/screenshots/category.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/category.png
--------------------------------------------------------------------------------
/docs/screenshots/commerce.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/commerce.png
--------------------------------------------------------------------------------
/docs/screenshots/entry.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/entry.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-guide-asset-field-mapping.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-guide-asset-field-mapping.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-guide-asset-finish.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-guide-asset-finish.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-guide-commerce-product-setup-mapping-multi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-guide-commerce-product-setup-mapping-multi.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-guide-commerce-product-setup-mapping-single.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-guide-commerce-product-setup-mapping-single.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-guide-commerce-product-setup-single.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-guide-commerce-product-setup-single.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-guide-commerce-product-success-single.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-guide-commerce-product-success-single.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-guide-finish.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-guide-finish.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-guide-mapping.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-guide-mapping.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-guide-setup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-guide-setup.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-logs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-logs.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-mapping.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-mapping.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-matrix-guide-finish1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-matrix-guide-finish1.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-matrix-guide-finish2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-matrix-guide-finish2.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-matrix-guide-mapping.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-matrix-guide-mapping.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-matrix-guide-setup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-matrix-guide-setup.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-matrix-guide-start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-matrix-guide-start.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-overview.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-setup-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-setup-1.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-setup-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-setup-2.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-setup-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-setup-3.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-setup-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-setup-4.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-setup-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-setup-5.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-setup-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-setup-6.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-setup-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-setup-7.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-setup-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-setup-8.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-setup-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-setup-9.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-setup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-setup.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-start.png
--------------------------------------------------------------------------------
/docs/screenshots/feedme-task.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/feedme-task.png
--------------------------------------------------------------------------------
/docs/screenshots/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/main.png
--------------------------------------------------------------------------------
/docs/screenshots/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/docs/screenshots/user.png
--------------------------------------------------------------------------------
/docs/troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting Tips
2 |
3 | ### Performance
4 |
5 | If you're experiencing slow processing for your feed, try the following:
6 |
7 | - Turn off `devMode`. Craft's built-in logging when devMode is switched on can greatly slow down the import process, and cause a high degree of memory overhead.
8 | - Similarly, when importing, make sure to disable the debug toolbar under your user account preferences. It can cause high memory overhead and other resource related issues.
9 | - Consider turning on the `compareContent` [configuration setting](get-started/configuration.md#configuration-options) to prevent unnecessary content overwriting.
10 | - Consider selecting the **Add Entries** option for duplication handling, depending on your requirements.
11 | - Consider turning off feeds’ **Backup** option, on large sites (if you have other rollback options).
12 | - Check if your feed is available as JSON. PHP can parse JSON much faster than XML documents.
13 |
14 | You may also need to adjust the `memory_limit` and `max_execution_time` values in your php.ini file if you run into memory issues.
15 |
16 | ### Unexpected Results
17 |
18 | If you're getting unexpected results when running an import, try to isolate the issue by selectively mapping fields until you have a bare-minimum import. For example, if you're mapping 20+ fields for an entry import, try to map just the **Title** field, and work your way through mapping additional fields until things stop working.
19 |
20 | ### Logging
21 |
22 | Feed Me records verbose logs as it reads feed data, matches elements, and applies changes. If you're experiencing issues or getting unexpected results with a Feed, consult the **Logs** tab first.
23 |
24 | 
25 |
26 | ::: tip
27 | For some steps, log messages are stored hierarchically, and collapses groups of messages. Expand a group using the **Show detail** action at the right edge of a message.
28 | :::
29 |
30 | ### Debugging
31 |
32 | Feed Me includes a special view to assist with debugging your feed, should you encounter issues or errors during an import. With [devMode](https://craftcms.com/docs/config-settings#devMode) enabled, click the “gear” in the problematic feed’s row to expand its utility drawer, then click **Debug**.
33 |
34 | 
35 |
36 | Debug output will be a combination of [`print_r`](https://www.php.net/manual/en/function.print-r.php)-formatted objects and log messages, providing you with as much information as possible about your feed settings, field-mappings, and data. If exceptions occur while processing the feed, they’ll appear on this page, too.
37 |
38 | ::: warning
39 | Debugging a feed attempts to actually run the import, so make sure you have [backups](./feature-tour/creating-your-feed#backup) on, or are working in a disposable environment!
40 | :::
41 |
--------------------------------------------------------------------------------
/src/base/DataType.php:
--------------------------------------------------------------------------------
1 | paginationNode) {
53 | return;
54 | }
55 |
56 | // Find the URL value in the feed
57 | $flatten = Hash::flatten($array, '/');
58 | $url = Hash::get($flatten, $feed->paginationNode);
59 |
60 | // resolve any aliases in the pagination URL
61 | $url = Craft::getAlias($url);
62 |
63 | // if the feed provides a root relative URL, make it whole again based on the feed.
64 | if ($url && UrlHelper::isRootRelativeUrl($url)) {
65 | $url = UrlHelper::hostInfo($feed->feedUrl) . $url;
66 | }
67 |
68 | // Replace the mapping value with the actual URL
69 | $feed->paginationUrl = $url;
70 | }
71 |
72 | /**
73 | * @inheritdoc
74 | */
75 | public function getSlice(int $offset, int $limit): iterable
76 | {
77 | $feedData = $this->feedData;
78 |
79 | if ($offset) {
80 | $feedData = array_slice($feedData, $offset);
81 | }
82 |
83 | if ($limit) {
84 | $feedData = array_slice($feedData, 0, $limit);
85 | }
86 |
87 | return $feedData;
88 | }
89 |
90 | /**
91 | * @inheritdoc
92 | */
93 | public function count(): int
94 | {
95 | return count($this->feedData);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/base/DataTypeInterface.php:
--------------------------------------------------------------------------------
1 | getLogs()->clear();
14 |
15 | return ExitCode::OK;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/controllers/BaseController.php:
--------------------------------------------------------------------------------
1 | requireAdmin(false);
27 |
28 | $settings = Plugin::$plugin->getSettings();
29 |
30 | return $this->renderTemplate('feed-me/settings/general', [
31 | 'settings' => $settings,
32 | 'readOnly' => !Craft::$app->getConfig()->getGeneral()->allowAdminChanges,
33 | ]);
34 | }
35 |
36 | /**
37 | * @return Response
38 | * @throws Exception
39 | */
40 | public function actionClearTasks(): Response
41 | {
42 | // Function to clear (delete) all stuck tasks.
43 | Craft::$app->getDb()->createCommand()
44 | ->delete('{{%queue}}')
45 | ->execute();
46 |
47 | return $this->redirect('feed-me/utilities');
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/controllers/LogsController.php:
--------------------------------------------------------------------------------
1 | getRequest()->getParam('show');
22 | $logEntries = Plugin::$plugin->getLogs()->getLogEntries($show);
23 |
24 | return $this->renderTemplate('feed-me/logs/index', [
25 | 'show' => $show,
26 | 'logEntries' => $logEntries,
27 | ]);
28 | }
29 |
30 | /**
31 | * @return Response
32 | */
33 | public function actionClear(): Response
34 | {
35 | Plugin::$plugin->getLogs()->clear();
36 |
37 | return $this->redirect('feed-me/logs');
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/datatypes/Atom.php:
--------------------------------------------------------------------------------
1 | data);
21 | }
22 |
23 | /**
24 | * @inheritdoc
25 | */
26 | public function getSlice(int $offset, int $limit): iterable
27 | {
28 | $slice = $this->data;
29 |
30 | if ($offset) {
31 | $slice = array_slice($slice, $offset);
32 | }
33 |
34 | if ($limit) {
35 | $slice = array_slice($slice, 0, $limit);
36 | }
37 |
38 | return $slice;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/datatypes/GoogleSheet.php:
--------------------------------------------------------------------------------
1 | data->getRawData($url, $feedId);
35 |
36 | if (!$response['success']) {
37 | $error = 'Unable to reach ' . $url . '. Message: ' . $response['error'];
38 |
39 | Plugin::error($error);
40 |
41 | return ['success' => false, 'error' => $error];
42 | }
43 |
44 | $data = $response['data'];
45 |
46 | try {
47 | $content = JsonHelper::decode($data, true);
48 |
49 | $headers = array_shift($content['values']);
50 | $rows = $content['values'];
51 |
52 | $array = [];
53 |
54 | foreach ($rows as $i => $row) {
55 | foreach ($row as $j => $column) {
56 | $key = $headers[$j];
57 |
58 | if (trim($key) == '') {
59 | $key = 'blank_heading_' . ($j + 1);
60 | }
61 |
62 | $array[$i][$key] = $column;
63 | }
64 | }
65 | } catch (Exception $e) {
66 | $error = 'Invalid data: ' . $e->getMessage();
67 |
68 | Plugin::error($error);
69 | Craft::$app->getErrorHandler()->logException($e);
70 |
71 | return ['success' => false, 'error' => $error];
72 | }
73 |
74 | // Make sure it's indeed an array!
75 | if (!is_array($array)) {
76 | $error = 'Invalid data: ' . Json::encode($array);
77 |
78 | Plugin::error($error);
79 |
80 | return ['success' => false, 'error' => $error];
81 | }
82 |
83 | // Look for and return only the items for primary element
84 | $primaryElement = Hash::get($settings, 'primaryElement');
85 |
86 | if ($primaryElement && $usePrimaryElement) {
87 | $array = Plugin::$plugin->data->findPrimaryElement($primaryElement, $array);
88 | }
89 |
90 | if ($array) {
91 | $this->feedData = $array;
92 | }
93 |
94 | return ['success' => true, 'data' => $array];
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/datatypes/Json.php:
--------------------------------------------------------------------------------
1 | data->getRawData($url, $feedId);
35 |
36 | if (!$response['success']) {
37 | $error = 'Unable to reach ' . $url . '. Message: ' . $response['error'];
38 |
39 | Plugin::error($error);
40 |
41 | return ['success' => false, 'error' => $error];
42 | }
43 |
44 | $data = $response['data'];
45 |
46 | // Parse the JSON string
47 | try {
48 | $array = JsonHelper::decode($data);
49 | } catch (InvalidArgumentException $e) {
50 | // See if we can get a better error with JsonParser
51 | $e = (new JsonParser())->lint($data) ?: $e;
52 | $error = 'Invalid JSON: ' . $e->getMessage();
53 | Plugin::error($error);
54 | Craft::$app->getErrorHandler()->logException($e);
55 | return ['success' => false, 'error' => $error];
56 | }
57 |
58 | // Make sure it's indeed an array!
59 | if (!is_array($array)) {
60 | $error = 'Invalid JSON: ' . json_last_error_msg();
61 | Plugin::error($error);
62 | return ['success' => false, 'error' => $error];
63 | }
64 |
65 | // if we have empty keys in the array - throw an error, that's not allowed
66 | $containsEmptyKeys = false;
67 | array_walk_recursive($array, function($value, $key) use (&$containsEmptyKeys) {
68 | if (trim($key) === '') {
69 | $containsEmptyKeys = true;
70 | }
71 | });
72 | if ($containsEmptyKeys) {
73 | $error = 'Invalid Data: data contains empty headings (keys)';
74 | Plugin::error($error);
75 | return ['success' => false, 'error' => $error];
76 | }
77 |
78 | // If using pagination, set it up here - we need to do this before messing around with the primary element
79 | $this->setupPaginationUrl($array, $settings);
80 |
81 | // Look for and return only the items for primary element
82 | $primaryElement = Hash::get($settings, 'primaryElement');
83 |
84 | if ($primaryElement && $usePrimaryElement) {
85 | $array = Plugin::$plugin->data->findPrimaryElement($primaryElement, $array);
86 | }
87 |
88 | if ($array) {
89 | $this->feedData = $array;
90 | }
91 |
92 | return ['success' => true, 'data' => $array];
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/datatypes/MissingDataType.php:
--------------------------------------------------------------------------------
1 |
18 | * @since 4.4.3
19 | */
20 | class MissingDataType extends DataType implements MissingComponentInterface
21 | {
22 | use MissingComponentTrait;
23 | }
24 |
--------------------------------------------------------------------------------
/src/datatypes/Rss.php:
--------------------------------------------------------------------------------
1 | data->getRawData($url, $feedId);
35 |
36 | if (!$response['success']) {
37 | $error = 'Unable to reach ' . $url . '. Message: ' . $response['error'];
38 |
39 | Plugin::error($error);
40 |
41 | return ['success' => false, 'error' => $error];
42 | }
43 |
44 | $data = $response['data'];
45 |
46 | // Parse the XML string into an array
47 | try {
48 | // Allow parsing errors to be caught
49 | libxml_use_internal_errors(true);
50 |
51 | $array = XmlParser::build($data);
52 | $array = XmlParser::toArray($array);
53 | } catch (Exception $e) {
54 | // Get a more useful error from parsing - if available
55 | if ($parseErrors = libxml_get_errors()) {
56 | $error = Craft::t('feed-me', 'Invalid XML: {e}: Line #{l}.', ['e' => $parseErrors[0]->message, 'l' => $parseErrors[0]->line]);
57 | } else {
58 | $error = Craft::t('feed-me', 'Invalid XML: {e}.', ['e' => $e->getMessage()]);
59 | }
60 |
61 | Plugin::error($error);
62 | Craft::$app->getErrorHandler()->logException($e);
63 |
64 | return ['success' => false, 'error' => $error];
65 | }
66 |
67 | // Make sure it's indeed an array!
68 | if (!is_array($array)) {
69 | $error = 'Invalid XML: ' . Json::encode($array);
70 |
71 | Plugin::error($error);
72 |
73 | return ['success' => false, 'error' => $error];
74 | }
75 |
76 | // If using pagination, set it up here - we need to do this before messing around with the primary element
77 | $this->setupPaginationUrl($array, $settings);
78 |
79 | // Look for and return only the items for primary element
80 | $primaryElement = Hash::get($settings, 'primaryElement');
81 |
82 | if ($primaryElement && $usePrimaryElement) {
83 | $array = Plugin::$plugin->data->findPrimaryElement($primaryElement, $array);
84 | }
85 |
86 | if ($array) {
87 | $this->feedData = $array;
88 | }
89 |
90 | return ['success' => true, 'data' => $array];
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/elements/GlobalSet.php:
--------------------------------------------------------------------------------
1 | getGlobals()->getEditableSets();
65 | $groups = [];
66 |
67 | foreach ($editable as $globalSet) {
68 | $groups[] = new ElementGroup([
69 | 'id' => $globalSet->id,
70 | 'model' => $globalSet,
71 | 'isSingleton' => true,
72 | ]);
73 | }
74 |
75 | return $groups;
76 | }
77 |
78 | /**
79 | * @inheritDoc
80 | */
81 | public function getQuery($settings, array $params = []): mixed
82 | {
83 | $query = GlobalSetElement::find()
84 | ->status(null)
85 | ->id($settings['elementGroup'][GlobalSetElement::class]['globalSet'])
86 | ->siteId(Hash::get($settings, 'siteId') ?: Craft::$app->getSites()->getPrimarySite()->id);
87 | Craft::configure($query, $params);
88 | return $query;
89 | }
90 |
91 | /**
92 | * @inheritDoc
93 | */
94 | public function setModel($settings): ElementInterface
95 | {
96 | return $this->element = new GlobalSetElement();
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/elements/Tag.php:
--------------------------------------------------------------------------------
1 | tags->getAllTagGroups();
70 | }
71 |
72 | /**
73 | * @inheritDoc
74 | */
75 | public function getQuery($settings, array $params = []): mixed
76 | {
77 | $query = TagElement::find()
78 | ->status(null)
79 | ->groupId($settings['elementGroup'][TagElement::class])
80 | ->siteId(Hash::get($settings, 'siteId') ?: Craft::$app->getSites()->getPrimarySite()->id);
81 | Craft::configure($query, $params);
82 | return $query;
83 | }
84 |
85 | /**
86 | * @inheritDoc
87 | */
88 | public function setModel($settings): ElementInterface
89 | {
90 | $this->element = new TagElement();
91 | $this->element->groupId = $settings['elementGroup'][TagElement::class];
92 |
93 | $siteId = Hash::get($settings, 'siteId');
94 |
95 | if ($siteId) {
96 | $this->element->siteId = $siteId;
97 | }
98 |
99 | return $this->element;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/errors/FeedException.php:
--------------------------------------------------------------------------------
1 |
16 | * @since 4.4.3
17 | */
18 | class FeedException extends UserException
19 | {
20 | /**
21 | * @return string the user-friendly name of this exception
22 | */
23 | public function getName()
24 | {
25 | return 'Feed Error';
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/events/AssetFilenameEvent.php:
--------------------------------------------------------------------------------
1 | fetchArrayValue();
49 | $default = $this->fetchDefaultArrayValue();
50 |
51 | if ($value === null) {
52 | return null;
53 | }
54 |
55 | $preppedData = [];
56 |
57 | $options = Hash::get($this->field, 'settings.options');
58 | $match = Hash::get($this->fieldInfo, 'options.match', 'value');
59 |
60 | foreach ($options as $option) {
61 | foreach ($value as $dataValue) {
62 | if ($dataValue === $option[$match]) {
63 | $preppedData[] = $option['value'];
64 | }
65 |
66 | // special case for when mapping by label, but also using a default value
67 | // which relies on $option['value']
68 | if (empty($dataValue) && in_array($option['value'], $default)) {
69 | $preppedData[] = $option['value'];
70 | }
71 | }
72 | }
73 |
74 | return $preppedData;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/fields/Country.php:
--------------------------------------------------------------------------------
1 | fetchValue();
51 |
52 | if ($value === null) {
53 | return null;
54 | }
55 |
56 | $value = (string) $value;
57 | $default = Hash::get($this->fieldInfo, 'default');
58 |
59 | $options = Craft::$app->getAddresses()->getCountryRepository()->getList(Craft::$app->language);
60 |
61 | $match = Hash::get($this->fieldInfo, 'options.match', 'value');
62 |
63 | // if we're matching by value - just look for the key in the $options array or check the default
64 | if (($match === 'value' && isset($options[$value])) || $default === $value) {
65 | return $value;
66 | }
67 |
68 | // if we're looking by label - look for the keys
69 | if ($match === 'label') {
70 | $found = array_filter($options, function($option) use ($value) {
71 | return strcasecmp($option, $value) === 0;
72 | });
73 |
74 | if (!empty($found)) {
75 | return array_key_first($found);
76 | }
77 | }
78 |
79 | if (empty($value)) {
80 | return $value;
81 | }
82 |
83 | return null;
84 | }
85 |
86 | /**
87 | * @inheritDoc
88 | */
89 | public function fetchValue(): mixed
90 | {
91 | return (string) parent::fetchValue();
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/fields/Date.php:
--------------------------------------------------------------------------------
1 | fetchValue();
50 |
51 | if ($value === null) {
52 | return null;
53 | }
54 |
55 | $formatting = Hash::get($this->fieldInfo, 'options.match');
56 |
57 | $dateValue = DateHelper::parseString($value, $formatting);
58 |
59 | if ($dateValue) {
60 | return $dateValue;
61 | }
62 |
63 | return $value;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/fields/DefaultField.php:
--------------------------------------------------------------------------------
1 | fetchValue();
48 |
49 | if ($value === null) {
50 | return null;
51 | }
52 |
53 | // Default fields expect strings, if it's an array for an odd reason, serialise it
54 | if (is_array($value)) {
55 | if (empty($value)) {
56 | $value = '';
57 | } else {
58 | $value = Json::encode($value);
59 | }
60 | }
61 |
62 | // If it's exactly an empty string, that's okay and allowed. Normalising this will set it to
63 | // null, which means it won't get imported. Sometimes we want to have empty strings
64 | if ($value !== '') {
65 | $value = $this->field->normalizeValue($value, $this->element);
66 | }
67 |
68 | // if we're setting empty values and the value is empty - return an empty string
69 | // otherwise HtmlField will serialize it to null, and we setEmptyValues won't take effect
70 | // https://github.com/craftcms/feed-me/issues/1321
71 | // if the normalizeValue above returns null, which can happen for e.g. plain text field and a value of a single space
72 | // we also need to ensure that an empty string is returned, not the value as that can be null after normalization
73 | // https://github.com/craftcms/feed-me/issues/1560
74 | if ($this->feed['setEmptyValues'] && empty($value)) {
75 | return '';
76 | }
77 |
78 | // Lastly, get each field to prepare values how they should
79 | return $this->field->serializeValue($value, $this->element);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/fields/Dropdown.php:
--------------------------------------------------------------------------------
1 | fetchValue();
50 |
51 | if ($value === null) {
52 | return null;
53 | }
54 |
55 | $value = (string) $value;
56 | $default = Hash::get($this->fieldInfo, 'default');
57 |
58 | $options = Hash::get($this->field, 'settings.options');
59 | $match = Hash::get($this->fieldInfo, 'options.match', 'value');
60 |
61 | foreach ($options as $option) {
62 | if (
63 | (isset($option['value']) && $value === $option[$match]) ||
64 | ($match === 'label' && $value === $default && $value === $option['value'])
65 | ) {
66 | return $option['value'];
67 | }
68 | }
69 |
70 | if ($this->feed['setEmptyValues'] && $value === '') {
71 | return $value;
72 | }
73 |
74 | return null;
75 | }
76 |
77 | /**
78 | * @inheritDoc
79 | */
80 | public function fetchValue(): mixed
81 | {
82 | return (string) parent::fetchValue();
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/fields/EntriesSubset.php:
--------------------------------------------------------------------------------
1 | fieldInfo, 'fields');
51 |
52 | if (!$fields) {
53 | return null;
54 | }
55 |
56 | foreach ($fields as $subFieldHandle => $subFieldInfo) {
57 | $value = DataHelper::fetchValue($this->feedData, $subFieldInfo, $this->feed);
58 | if ($value !== null) {
59 | $preppedData[$subFieldHandle] = $value;
60 | }
61 | }
62 |
63 | // Protect against sending an empty array
64 | if (!$preppedData) {
65 | return null;
66 | }
67 |
68 | return $preppedData;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/fields/Icon.php:
--------------------------------------------------------------------------------
1 | fetchValue();
50 |
51 | if ($value === null) {
52 | return null;
53 | }
54 |
55 | // Default fields expect strings, if it's an array for an odd reason, serialise it
56 | if (is_array($value)) {
57 | if (empty($value)) {
58 | $value = '';
59 | } else {
60 | $value = Json::encode($value);
61 | }
62 | }
63 |
64 | // If it's exactly an empty string, that's okay and allowed. Normalising this will set it to
65 | // null, which means it won't get imported. Sometimes we want to have empty strings
66 | if ($value !== '') {
67 | $value = $this->field->normalizeValue($value, $this->element);
68 | }
69 |
70 | // if we're setting empty values and the value is an empty string - return it
71 | // otherwise HtmlField will serialize it to null, and we setEmptyValues won't take effect
72 | // https://github.com/craftcms/feed-me/issues/1321
73 | if ($this->feed['setEmptyValues'] === 1 && $value === '') {
74 | return $value;
75 | }
76 |
77 | // Lastly, get each field to prepare values how they should
78 | return $this->field->serializeValue($value, $this->element);
79 | }
80 |
81 | /**
82 | * @inheritDoc
83 | */
84 | public function fetchValue(): mixed
85 | {
86 | return (string) parent::fetchValue();
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/fields/Lightswitch.php:
--------------------------------------------------------------------------------
1 | fetchValue();
49 |
50 | if ($value === null) {
51 | return null;
52 | }
53 |
54 | return $this->parseValue($value);
55 | }
56 |
57 | public function parseValue($value)
58 | {
59 | return BaseHelper::parseBoolean($value);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/fields/Linkit.php:
--------------------------------------------------------------------------------
1 | fieldInfo, 'fields');
52 |
53 | if (!$fields) {
54 | return null;
55 | }
56 |
57 | foreach ($fields as $subFieldHandle => $subFieldInfo) {
58 | $preppedData[$subFieldHandle] = DataHelper::fetchValue($this->feedData, $subFieldInfo, $this->feed);
59 | }
60 |
61 | if (empty(
62 | array_filter($preppedData, function($val) {
63 | return $val !== null;
64 | })
65 | )) {
66 | return null;
67 | }
68 |
69 | if ($preppedData) {
70 | // Handle Link Type
71 | $preppedData['type'] = empty($preppedData['type'] ?? '') ? 'presseddigital\linkit\models\Url' : $preppedData['type'];
72 | if (!str_contains($preppedData['type'], '\\')) {
73 | $preppedData['type'] = 'presseddigital\\linkit\\models\\' . ucfirst(strtolower(trim($preppedData['type'])));
74 | }
75 |
76 | // Handle Link Target
77 | $preppedData['target'] = trim(empty($preppedData['target'] ?? '') ? '' : $preppedData['target']);
78 | $preppedData['target'] = $preppedData['target'] && $preppedData['target'] != '_self' ? 1 : '';
79 | }
80 |
81 | // Protect against sending an empty array
82 | if (!$preppedData) {
83 | return null;
84 | }
85 |
86 | return $preppedData;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/fields/MissingField.php:
--------------------------------------------------------------------------------
1 |
18 | * @since 4.4.3
19 | */
20 | class MissingField extends Field implements MissingComponentInterface
21 | {
22 | use MissingComponentTrait;
23 | }
24 |
--------------------------------------------------------------------------------
/src/fields/Money.php:
--------------------------------------------------------------------------------
1 | fetchValue();
51 |
52 | if ($value === null) {
53 | return null;
54 | }
55 |
56 | $localized = Hash::get($this->fieldInfo, 'options.localized');
57 |
58 | if ($localized) {
59 | // value provided in the feed should be localised (like with the Number field)
60 |
61 | // for example if the site you're importing to is Dutch (nl),
62 | // you checked the "Data provided for this localized for the site the feed is for" checkbox on the feed mapping screen
63 | // and your money field is in EUR,
64 | // the amount of: one thousand two hundred thirty-four euro and fifty-six cents
65 | // should be: 1.234,56 in your feed;
66 | $site = Craft::$app->getSites()->getSiteById($this->feed['siteId']);
67 | $siteLocaleId = $site->getLocale()->id;
68 | } else {
69 | // the values in the feed are in a float-like notation
70 |
71 | // for example if the site you're importing to is Dutch (nl),
72 | // you DIDN'T check the "Data provided for this localized for the site the feed is for" checkbox on the feed mapping screen
73 | // and your money field is in EUR,
74 | // one thousand two hundred thirty-four euro and fifty-six cents
75 | // should be: 1234.56 in your feed;
76 | $siteLocaleId = 'en';
77 | }
78 |
79 | return [
80 | 'value' => Localization::normalizeNumber($value, $siteLocaleId),
81 | 'locale' => 'en',
82 | ];
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/fields/MultiSelect.php:
--------------------------------------------------------------------------------
1 | fetchArrayValue();
49 | $default = $this->fetchDefaultArrayValue();
50 |
51 | if ($value === null) {
52 | return null;
53 | }
54 |
55 | $preppedData = [];
56 |
57 | $options = Hash::get($this->field, 'settings.options');
58 | $match = Hash::get($this->fieldInfo, 'options.match', 'value');
59 |
60 | foreach ($options as $option) {
61 | // Exclude optgroup from available values to match against
62 | if (isset($option['optgroup'])) {
63 | continue;
64 | }
65 | foreach ($value as $dataValue) {
66 | if ((!empty($dataValue) || is_numeric($dataValue)) && $dataValue === $option[$match]) {
67 | $preppedData[] = $option['value'];
68 | }
69 | // special case for when mapping by label, but also using a default value
70 | // which relies on $option['value']
71 | if (empty($dataValue) && in_array($option['value'], $default)) {
72 | $preppedData[] = $option['value'];
73 | }
74 | }
75 | }
76 |
77 | return $preppedData;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/fields/Number.php:
--------------------------------------------------------------------------------
1 | fetchValue();
49 |
50 | if ($value === null) {
51 | return null;
52 | }
53 |
54 | return $this->parseValue($value);
55 | }
56 |
57 | /**
58 | * @param $value
59 | * @return mixed
60 | */
61 | public function parseValue($value): mixed
62 | {
63 | return Localization::normalizeNumber($value);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/fields/RadioButtons.php:
--------------------------------------------------------------------------------
1 | fetchValue();
51 | $default = Hash::get($this->fieldInfo, 'default');
52 |
53 | $options = Hash::get($this->field, 'settings.options');
54 | $match = Hash::get($this->fieldInfo, 'options.match', 'value');
55 |
56 | foreach ($options as $option) {
57 | if (
58 | (isset($option['value']) && $value === $option[$match]) ||
59 | ($match === 'label' && $value === $default && $value === $option['value'])
60 | ) {
61 | return $option['value'];
62 | }
63 | }
64 |
65 | if (empty($value)) {
66 | return $value;
67 | }
68 |
69 | return null;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/fields/SmartMap.php:
--------------------------------------------------------------------------------
1 | fieldInfo, 'fields');
51 |
52 | if (!$fields) {
53 | return null;
54 | }
55 |
56 | foreach ($fields as $subFieldHandle => $subFieldInfo) {
57 | $value = DataHelper::fetchValue($this->feedData, $subFieldInfo, $this->feed);
58 | if ($value !== null) {
59 | $preppedData[$subFieldHandle] = $value;
60 | }
61 | }
62 |
63 | // Protect against sending an empty array
64 | if (!$preppedData) {
65 | return null;
66 | }
67 |
68 | return $preppedData;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/fields/Time.php:
--------------------------------------------------------------------------------
1 | fetchValue();
50 |
51 | if ($value === null) {
52 | return null;
53 | }
54 |
55 | $formatting = Hash::get($this->fieldInfo, 'options.match') ?? 'auto';
56 |
57 | $timeValue = DateHelper::parseString($value, $formatting);
58 |
59 | if ($timeValue) {
60 | return $timeValue;
61 | }
62 |
63 | return $value;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/fields/TypedLink.php:
--------------------------------------------------------------------------------
1 | fieldInfo, 'fields');
51 |
52 | if (!$fields) {
53 | return null;
54 | }
55 |
56 | foreach ($fields as $subFieldHandle => $subFieldInfo) {
57 | $preppedData[$subFieldHandle] = DataHelper::fetchValue($this->feedData, $subFieldInfo, $this->feed);
58 | }
59 |
60 | if (empty(
61 | array_filter($preppedData, function($val) {
62 | return $val !== null;
63 | })
64 | )) {
65 | return null;
66 | }
67 |
68 | // Protect against sending an empty array
69 | if (!$preppedData) {
70 | return null;
71 | }
72 |
73 | return $preppedData;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/helpers/DuplicateHelper.php:
--------------------------------------------------------------------------------
1 | createTables();
15 |
16 | return true;
17 | }
18 |
19 | public function safeDown(): bool
20 | {
21 | $this->removeTables();
22 |
23 | return true;
24 | }
25 |
26 | // Protected Methods
27 | // =========================================================================
28 |
29 | protected function createTables(): void
30 | {
31 | $this->createTable('{{%feedme_feeds}}', [
32 | 'id' => $this->primaryKey(),
33 | 'name' => $this->string()->notNull(),
34 | 'feedUrl' => $this->text()->notNull(),
35 | 'feedType' => $this->string(),
36 | 'primaryElement' => $this->string(),
37 | 'elementType' => $this->string()->notNull(),
38 | 'elementGroup' => $this->text(),
39 | 'siteId' => $this->string(),
40 | 'sortOrder' => $this->smallInteger()->unsigned(),
41 | 'singleton' => $this->boolean()->notNull()->defaultValue(false),
42 | 'duplicateHandle' => $this->text(),
43 | 'updateSearchIndexes' => $this->boolean()->notNull()->defaultValue(true),
44 | 'paginationNode' => $this->text(),
45 | 'fieldMapping' => $this->mediumText(),
46 | 'fieldUnique' => $this->text(),
47 | 'passkey' => $this->string()->notNull(),
48 | 'backup' => $this->boolean()->notNull()->defaultValue(false),
49 | 'setEmptyValues' => $this->boolean()->notNull()->defaultValue(false),
50 |
51 | 'dateCreated' => $this->dateTime()->notNull(),
52 | 'dateUpdated' => $this->dateTime()->notNull(),
53 | 'uid' => $this->uid(),
54 | ]);
55 |
56 | // @see \craft\feedme\migrations\m240611_134740_create_logs_table
57 | $this->createTable('{{%feedme_logs}}', [
58 | 'id' => $this->bigPrimaryKey(),
59 | 'level' => $this->integer(),
60 | 'category' => $this->string(),
61 | 'log_time' => $this->double(),
62 | 'prefix' => $this->text(),
63 | 'message' => $this->text(),
64 | ]);
65 |
66 | $this->createIndex('idx_log_level', '{{%feedme_logs}}', 'level');
67 | $this->createIndex('idx_log_category', '{{%feedme_logs}}', 'category');
68 | }
69 |
70 | protected function removeTables(): void
71 | {
72 | $this->dropTableIfExists('{{%feedme_feeds}}');
73 | $this->dropTableIfExists('{{%feedme_logs}}');
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/migrations/m230123_152413_set_empty_values.php:
--------------------------------------------------------------------------------
1 | db->columnExists('{{%feedme_feeds}}', 'setEmptyValues')) {
18 | $this->addColumn('{{%feedme_feeds}}', 'setEmptyValues', $this->boolean()->defaultValue(false)->notNull()->after('backup'));
19 | }
20 | }
21 |
22 | /**
23 | * @inheritdoc
24 | */
25 | public function safeDown()
26 | {
27 | if ($this->db->columnExists('{{%feedme_feeds}}', 'setEmptyValues')) {
28 | $this->dropColumn('{{%feedme_feeds}}', 'setEmptyValues');
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/migrations/m240523_145259_field_mapping_column_change.php:
--------------------------------------------------------------------------------
1 | db->columnExists('{{%feedme_feeds}}', 'fieldMapping')) {
18 | $this->alterColumn('{{%feedme_feeds}}', 'fieldMapping', $this->mediumText());
19 | }
20 |
21 | return true;
22 | }
23 |
24 | /**
25 | * @inheritdoc
26 | */
27 | public function safeDown(): bool
28 | {
29 | if ($this->db->columnExists('{{%feedme_feeds}}', 'fieldMapping')) {
30 | $this->alterColumn('{{%feedme_feeds}}', 'fieldMapping', $this->text());
31 | }
32 |
33 | return true;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/migrations/m240611_134740_create_logs_table.php:
--------------------------------------------------------------------------------
1 | createTable('{{%feedme_logs}}', [
19 | 'id' => $this->bigPrimaryKey(),
20 | 'level' => $this->integer(),
21 | 'category' => $this->string(),
22 | 'log_time' => $this->double(),
23 | 'prefix' => $this->text(),
24 | 'message' => $this->text(),
25 | ]);
26 |
27 | $this->createIndex('idx_log_level', '{{%feedme_logs}}', 'level');
28 | $this->createIndex('idx_log_category', '{{%feedme_logs}}', 'category');
29 |
30 | return true;
31 | }
32 |
33 | /**
34 | * @inheritdoc
35 | */
36 | public function safeDown(): bool
37 | {
38 | $this->dropTableIfExists('{{%feedme_logs}}');
39 |
40 | return true;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/models/ElementGroup.php:
--------------------------------------------------------------------------------
1 | Craft::t('feed-me', 'Your Email'),
64 | ];
65 | }
66 |
67 | /**
68 | *
69 | * @return array
70 | */
71 | public function rules(): array
72 | {
73 | return [
74 | [['fromEmail', 'feedIssue', 'message'], 'required'],
75 | [['fromEmail'], 'email'],
76 | [['fromEmail'], 'string', 'min' => 5, 'max' => 255],
77 | [['attachment'], 'file', 'maxSize' => 3145728],
78 | ];
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/models/Settings.php:
--------------------------------------------------------------------------------
1 | [
37 | 'User-Agent' => 'Feed Me',
38 | ],
39 | ];
40 |
41 | /**
42 | * @var bool
43 | */
44 | public bool $compareContent = true;
45 |
46 | /**
47 | * @var string
48 | */
49 | public string $skipUpdateFieldHandle = '';
50 |
51 | /**
52 | * @var int
53 | */
54 | public int $backupLimit = 100;
55 |
56 | /**
57 | * @var string
58 | */
59 | public string $dataDelimiter = '-|-';
60 |
61 | /**
62 | * @var string
63 | */
64 | public string $csvColumnDelimiter = ',';
65 |
66 | /**
67 | * @var bool
68 | */
69 | public bool $parseTwig = false;
70 |
71 | /**
72 | * @var array
73 | */
74 | public array $feedOptions = [];
75 |
76 | /**
77 | * @var int
78 | */
79 | public int $sleepTime = 0;
80 |
81 | /**
82 | * @var bool|string
83 | */
84 | public bool|string $logging = true;
85 |
86 | /**
87 | * @var bool
88 | */
89 | public bool $runGcBeforeFeed = false;
90 |
91 | /**
92 | * @var int|null
93 | */
94 | public ?int $queueTtr = null;
95 |
96 | /**
97 | * @var int|null
98 | */
99 | public ?int $queueMaxRetry = null;
100 |
101 | /**
102 | * @var bool
103 | */
104 | public bool $assetDownloadCurl = false;
105 |
106 | /**
107 | * @var bool
108 | * @since 5.9.0
109 | */
110 | public bool $assetDownloadGuzzle = false;
111 | }
112 |
--------------------------------------------------------------------------------
/src/records/FeedRecord.php:
--------------------------------------------------------------------------------
1 | getSettings();
27 |
28 | // Get the config item from the global settings
29 | $configItem = Hash::get($settings, $key);
30 |
31 | // Or, check if there's a setting set per-feed
32 | if ($feedId) {
33 | $configFeedItem = Hash::get($settings, 'feedOptions.' . $feedId . '.' . $key);
34 |
35 | if (isset($configFeedItem)) {
36 | $configItem = $configFeedItem;
37 | }
38 | }
39 |
40 | return $configItem;
41 | }
42 |
43 | /**
44 | * @param null $feedId
45 | * @return Client
46 | */
47 | public function createGuzzleClient($feedId = null): Client
48 | {
49 | $options = $this->getConfig('clientOptions', $feedId);
50 |
51 | return Craft::createGuzzleClient($options);
52 | }
53 |
54 | /**
55 | * @param null $feedId
56 | * @return array|ArrayAccess|mixed|null
57 | */
58 | public function getRequestOptions($feedId = null): mixed
59 | {
60 | return $this->getConfig('requestOptions', $feedId);
61 | }
62 |
63 | /**
64 | * @param $dateTime
65 | * @return DateTime|false
66 | * @throws \Exception
67 | */
68 | public function formatDateTime($dateTime): DateTime|bool
69 | {
70 | return DateTimeHelper::toDateTime($dateTime);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/templates/_includes/elements/assets/column.html:
--------------------------------------------------------------------------------
1 | {% set group = craft.app.volumes.getVolumeById(feed.elementGroup[elementType]) %}
2 |
3 | {{ group.name ?? '' }}
--------------------------------------------------------------------------------
/src/templates/_includes/elements/assets/groups.html:
--------------------------------------------------------------------------------
1 | {% import '_includes/forms' as forms %}
2 |
3 | {% set assets = element.groups %}
4 |
5 | {{ forms.selectField({
6 | label: "Asset Volume"|t('feed-me'),
7 | instructions: 'Choose the asset volume you want to save your feed data into.'|t('feed-me'),
8 | id: 'elementGroup-' ~ elementType,
9 | name: 'elementGroup[' ~ elementType ~ ']',
10 | options: craft.feedme.getSelectOptions(assets),
11 | value: feed.elementGroup[elementType] ?? '',
12 | errors: feed.getErrors('elementGroup'),
13 | required: true,
14 | }) }}
15 |
--------------------------------------------------------------------------------
/src/templates/_includes/elements/calendar-events/column.html:
--------------------------------------------------------------------------------
1 | {% set calendarId = feed.elementGroup[elementType] %}
2 | {% set calendar = craft.calendar.calendar({ id: calendarId }) %}
3 |
4 | {{ calendar.name ?? '' }}
5 |
--------------------------------------------------------------------------------
/src/templates/_includes/elements/calendar-events/groups.html:
--------------------------------------------------------------------------------
1 | {% import '_includes/forms' as forms %}
2 |
3 | {% set calendars = element.groups %}
4 |
5 | {{ forms.selectField({
6 | label: 'Calendar'|t('feed-me'),
7 | instructions: 'Choose the calendar you want to save your feed data into.'|t('feed-me'),
8 | id: 'elementGroup-' ~ elementType,
9 | name: 'elementGroup[' ~ elementType ~ ']',
10 | options: craft.feedme.getSelectOptions(calendars),
11 | value: feed.elementGroup[elementType] ?? '',
12 | errors: feed.getErrors('elementGroup'),
13 | required: true,
14 | }) }}
15 |
--------------------------------------------------------------------------------
/src/templates/_includes/elements/categories/column.html:
--------------------------------------------------------------------------------
1 | {% set group = craft.app.categories.getGroupById(feed.elementGroup[elementType]) %}
2 |
3 | {{ group.name ?? '' }}
--------------------------------------------------------------------------------
/src/templates/_includes/elements/categories/groups.html:
--------------------------------------------------------------------------------
1 | {% import '_includes/forms' as forms %}
2 |
3 | {% set categories = element.groups %}
4 |
5 | {{ forms.selectField({
6 | label: "Category Group"|t('feed-me'),
7 | instructions: 'Choose the category group you want to save your feed data into.'|t('feed-me'),
8 | id: 'elementGroup-' ~ elementType,
9 | name: 'elementGroup[' ~ elementType ~ ']',
10 | options: craft.feedme.getSelectOptions(categories),
11 | value: feed.elementGroup[elementType] ?? '',
12 | errors: feed.getErrors('elementGroup'),
13 | required: true,
14 | }) }}
15 |
--------------------------------------------------------------------------------
/src/templates/_includes/elements/commerce-products/column.html:
--------------------------------------------------------------------------------
1 | {% set group = craft.commerce.productTypes.getProductTypeById(feed.elementGroup[elementType]) %}
2 |
3 | {{ group.name ?? '' }}
--------------------------------------------------------------------------------
/src/templates/_includes/elements/commerce-products/groups.html:
--------------------------------------------------------------------------------
1 | {% import '_includes/forms' as forms %}
2 |
3 | {% set productTypes = element.groups %}
4 |
5 | {{ forms.selectField({
6 | label: "Commerce Product Type"|t('feed-me'),
7 | instructions: 'Choose the Commerce product type you want to to save your feed data into.'|t('feed-me'),
8 | id: 'elementGroup-' ~ elementType,
9 | name: 'elementGroup[' ~ elementType ~ ']',
10 | options: craft.feedme.getSelectOptions(productTypes),
11 | value: feed.elementGroup[elementType] ?? '',
12 | errors: feed.getErrors('elementGroup'),
13 | required: true,
14 | }) }}
15 |
--------------------------------------------------------------------------------
/src/templates/_includes/elements/digital-products/column.html:
--------------------------------------------------------------------------------
1 | {% set group = craft.digitalProducts.getPlugin().getProductTypes().getProductTypeById(feed.elementGroup[elementType]) %}
2 |
3 | {{ group.name ?? '' }}
--------------------------------------------------------------------------------
/src/templates/_includes/elements/digital-products/groups.html:
--------------------------------------------------------------------------------
1 | {% import '_includes/forms' as forms %}
2 |
3 | {% set categories = element.groups %}
4 |
5 | {{ forms.selectField({
6 | label: "Digital Product Group"|t('feed-me'),
7 | instructions: 'Choose the digital product type you want to save your feed data into.'|t('feed-me'),
8 | id: 'elementGroup-' ~ elementType,
9 | name: 'elementGroup[' ~ elementType ~ ']',
10 | options: craft.feedme.getSelectOptions(categories),
11 | value: feed.elementGroup[elementType] ?? '',
12 | errors: feed.getErrors('elementGroup'),
13 | required: true,
14 | }) }}
15 |
--------------------------------------------------------------------------------
/src/templates/_includes/elements/entries/column.html:
--------------------------------------------------------------------------------
1 | {% set sectionId = feed.elementGroup[elementType].section %}
2 | {% set entryTypeId = feed.elementGroup[elementType].entryType %}
3 |
4 | {% if sectionId and entryTypeId %}
5 | {% set section = craft.app.entries.getSectionById(sectionId) %}
6 | {% set entryType = craft.app.entries.getEntryTypeById(entryTypeId) %}
7 |
8 | {% if section and entryType %}
9 | {{ section.name }}
10 | {{ entryType.name }}
11 | {% endif %}
12 | {% endif %}
13 |
--------------------------------------------------------------------------------
/src/templates/_includes/elements/entries/groups.html:
--------------------------------------------------------------------------------
1 | {% import '_includes/forms' as forms %}
2 |
3 | {% set sections = element.groups %}
4 |
5 | {% set sectionEntryTypes = [] %}
6 |
7 | {# Create a section-indexed array of element types #}
8 | {% set entryTypes = [] %}
9 | {% for section in sections %}
10 | {% set options = craft.feedme.getSelectOptions(section.model.getEntryTypes()) %}
11 |
12 | {# We have to prefix the index, otherwise Twig doesn't maintain numbered index correctly #}
13 | {% set entryTypes = entryTypes|merge({ ('item_' ~ section.id): options }) %}
14 | {% endfor %}
15 |
16 | {% set sectionId = null %}
17 | {% set entryTypeId = null %}
18 |
19 | {# Load saved values for feed #}
20 | {% if feed.elementGroup[elementType] is defined %}
21 | {% set sectionId = feed.elementGroup[elementType].section ?? null %}
22 | {% set entryTypeId = feed.elementGroup[elementType].entryType ?? null %}
23 | {% endif %}
24 |
25 | {% if sectionId %}
26 | {% set section = craft.app.entries.getSectionById(sectionId) %}
27 |
28 | {% if section %}
29 | {% set sectionEntryTypes = section.getEntryTypes() %}
30 | {% endif %}
31 | {% elseif sections[0] is defined %}
32 | {% set sectionEntryTypes = sections[0].model.getEntryTypes() ?? null %}
33 | {% endif %}
34 |
35 |
36 | {{ forms.selectField({
37 | label: "Section"|t('feed-me'),
38 | instructions: 'Choose the Section you want to save your feed data to.'|t('feed-me'),
39 | class: 'element-parent-group',
40 | id: 'elementGroup-' ~ elementType ~ '-section',
41 | name: 'elementGroup[' ~ elementType ~ '][section]',
42 | options: craft.feedme.getSelectOptions(sections|map(s => s.model)),
43 | value: sectionId ?? '',
44 | errors: feed.getErrors('elementGroup'),
45 | required: true,
46 | }) }}
47 |
48 | {{ forms.selectField({
49 | label: "Entry Type"|t('feed-me'),
50 | instructions: 'Choose the Entry Type you want to save your feed data into.'|t('feed-me'),
51 | class: 'element-child-group',
52 | id: 'elementGroup-' ~ elementType ~ '-entryType',
53 | name: 'elementGroup[' ~ elementType ~ '][entryType]',
54 | options: craft.feedme.getSelectOptions(sectionEntryTypes),
55 | value: entryTypeId ?? '',
56 | errors: feed.getErrors('elementGroup'),
57 | required: true,
58 | }) }}
59 |
60 |
--------------------------------------------------------------------------------
/src/templates/_includes/elements/global-sets/column.html:
--------------------------------------------------------------------------------
1 | {% set globalSetId = feed.elementGroup[elementType].globalSet %}
2 |
3 | {% if globalSetId %}
4 | {% set globalSet = craft.app.globals.getSetById(globalSetId) %}
5 |
6 | {% if globalSet %}
7 | {{ globalSet.name }}
8 | {% endif %}
9 | {% endif %}
10 |
--------------------------------------------------------------------------------
/src/templates/_includes/elements/global-sets/groups.html:
--------------------------------------------------------------------------------
1 | {% import '_includes/forms' as forms %}
2 |
3 | {% set globalSetId = feed.elementGroup[elementType].globalSet ?? null %}
4 |
5 |
6 | {{ forms.selectField({
7 | label: 'Global Set'|t('feed-me'),
8 | instructions: 'Choose the global set you want to save your feed data to.'|t('feed-me'),
9 | class: 'element-parent-group',
10 | id: 'elementGroup-' ~ elementType ~ '-global-set',
11 | name: 'elementGroup[' ~ elementType ~ '][globalSet]',
12 | options: craft.feedme.getSelectOptions(element.groups|map(s => s.model)),
13 | value: globalSetId,
14 | errors: feed.getErrors('elementGroup'),
15 | required: true,
16 | }) }}
17 |
18 |
--------------------------------------------------------------------------------
/src/templates/_includes/elements/global-sets/map.html:
--------------------------------------------------------------------------------
1 | {% import '_includes/forms' as forms %}
2 | {% import 'feed-me/_macros' as feedMeMacro %}
3 |
4 | {% if feed.elementGroup %}
5 | {% set globalSetId = feed.elementGroup[feed.elementType].globalSet %}
6 | {% set globalSet = craft.app.globals.getSetById(globalSetId) %}
7 | {% endif %}
8 |
9 | {% set tabs = [] %}
10 |
11 | {% if globalSet.fieldLayoutId %}
12 | {% set tabs = craft.app.fields.getLayoutById(globalSet.fieldLayoutId).getTabs() %}
13 |
14 | {% for tab in tabs %}
15 |
16 |
17 | {{ tab.name }} Fields
18 |
19 |
20 |
21 | {{ 'Field'|t('feed-me') }}
22 | {{ 'Feed Element'|t('feed-me') }}
23 | {{ 'Default Value'|t('feed-me') }}
24 |
25 |
26 | {% for layoutField in tab.getElements()|filter(e => e is instance of('craft\\fieldlayoutelements\\CustomField')) %}
27 | {% set field = layoutField.getField() %}
28 | {% set fieldClass = craft.feedme.fields.getRegisteredField(className(field)) %}
29 | {% set template = fieldClass.getMappingTemplate() %}
30 |
31 | {% set variables = { name: field.name, handle: field.handle, feed: feed, feedData: feedData, field: field, fieldClass: fieldClass } %}
32 |
33 | {% include template ignore missing with variables only %}
34 | {% endfor %}
35 |
36 |
37 | {% endfor %}
38 | {% endif %}
39 |
--------------------------------------------------------------------------------
/src/templates/_includes/elements/tag/column.html:
--------------------------------------------------------------------------------
1 | {% set group = craft.app.tags.getTagGroupById(feed.elementGroup[elementType]) %}
2 |
3 | {{ group.name ?? '' }}
--------------------------------------------------------------------------------
/src/templates/_includes/elements/tag/groups.html:
--------------------------------------------------------------------------------
1 | {% import '_includes/forms' as forms %}
2 |
3 | {% set tags = element.groups %}
4 |
5 | {{ forms.selectField({
6 | label: "Tag Group"|t('feed-me'),
7 | instructions: 'Choose the tag group you want to save your feed data into.'|t('feed-me'),
8 | id: 'elementGroup-' ~ elementType,
9 | name: 'elementGroup[' ~ elementType ~ ']',
10 | options: craft.feedme.getSelectOptions(tags),
11 | value: feed.elementGroup[elementType] ?? '',
12 | errors: feed.getErrors('elementGroup'),
13 | required: true,
14 | }) }}
15 |
--------------------------------------------------------------------------------
/src/templates/_includes/elements/user/column.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/templates/_includes/elements/user/column.html
--------------------------------------------------------------------------------
/src/templates/_includes/elements/user/groups.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/templates/_includes/elements/user/groups.html
--------------------------------------------------------------------------------
/src/templates/_includes/fields/assets-create.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% if field is defined %}
15 | {% set default = default ?? {
16 | type: 'elementselect',
17 | options: {
18 | elementType: fieldClass.elementType,
19 | selectionLabel: "Default Asset"|t('feed-me'),
20 | },
21 | } %}
22 | {% endif %}
23 |
24 | {# Get any sub-fields for the element. Also check to go only one level deep #}
25 | {% if field is defined and isSubElementField is not defined %}
26 | {% set elementFields = [] %}
27 |
28 | {# Prevent infinite loop by only allow elements that are not the same as this #}
29 | {% for elementField in craft.feedme.getElementLayoutByField(className(field), field) %}
30 | {% if craft.feedme.supportedSubField(className(elementField)) %}
31 | {% set elementFields = elementFields|merge([ elementField ]) %}
32 | {% endif %}
33 | {% endfor %}
34 | {% endif %}
35 |
36 | {% extends 'feed-me/_includes/fields/_base' %}
37 |
38 | {% block extraSettings %}
39 |
40 |
41 | {{ forms.selectField({
42 | label: 'If asset already exists:'|t('feed-me'),
43 | name: 'options[conflict]',
44 | class: '',
45 | options: [
46 | { value: 'index', label: 'Use existing asset' },
47 | { value: 'replace', label: 'Replace existing asset' },
48 | { value: 'create', label: 'Keep both (rename feed data)' },
49 | ],
50 | value: hash_get(feed.fieldMapping, optionsPath ~ '.conflict') ?: '',
51 | }) }}
52 |
53 | {% endblock %}
54 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/assets-folder.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% set default = default ?? {
15 | type: 'text',
16 | } %}
17 |
18 | {% extends 'feed-me/_includes/fields/_base' %}
19 |
20 | {% block extraSettings %}
21 |
22 | {{ feedMeMacro.checkbox({
23 | label: 'Create folders if they do not exist'|t('feed-me'),
24 | name: 'options[create]',
25 | value: 1,
26 | checked: hash_get(feed.fieldMapping, optionsPath ~ '.create') ?: '',
27 | }) }}
28 |
29 | {% endblock %}
30 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/assets.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% if field is defined %}
15 | {% set default = default ?? {
16 | type: 'elementselect',
17 | options: {
18 | elementType: fieldClass.elementType,
19 | selectionLabel: "Default Asset"|t('feed-me'),
20 | },
21 | } %}
22 | {% endif %}
23 |
24 | {# Get any sub-fields for the element. Also check to go only one level deep #}
25 | {% if field is defined and isSubElementField is not defined %}
26 | {% set elementFields = [] %}
27 |
28 | {# Prevent infinite loop by only allow elements that are not the same as this #}
29 | {% for elementField in craft.feedme.getElementLayoutByField(className(field), field) %}
30 | {% if craft.feedme.supportedSubField(className(elementField)) %}
31 | {% set elementFields = elementFields|merge([ elementField ]) %}
32 | {% endif %}
33 | {% endfor %}
34 | {% endif %}
35 |
36 | {% extends 'feed-me/_includes/fields/_base' %}
37 |
38 | {% block extraSettings %}
39 |
40 |
41 | {{ 'Data provided for this asset is:'|t('feed-me') }}
42 |
43 | {{ forms.selectField({
44 | name: 'options[match]',
45 | class: '',
46 | value: hash_get(feed.fieldMapping, optionsPath ~ '.match') ?: '',
47 | options: [
48 | { value: 'filename', label: 'Filename'|t('feed-me') },
49 | { value: 'id', label: 'ID'|t('feed-me') },
50 | ],
51 | }) }}
52 |
53 |
54 |
55 | {{ feedMeMacro.checkbox({
56 | label: 'Create asset from URL
, if exists: '|t('feed-me')|raw,
57 | name: 'options[upload]',
58 | value: 1,
59 | checked: hash_get(feed.fieldMapping, optionsPath ~ '.upload') ?: '',
60 | }) }}
61 |
62 | {{ forms.selectField({
63 | name: 'options[conflict]',
64 | class: '',
65 | options: [
66 | { value: 'index', label: 'Use existing asset' },
67 | { value: 'replace', label: 'Replace existing asset' },
68 | { value: 'create', label: 'Keep both (rename feed data)' },
69 | ],
70 | value: hash_get(feed.fieldMapping, optionsPath ~ '.conflict') ?: '',
71 | }) }}
72 |
73 |
74 | {{ forms.selectField({
75 | label: 'Use this filename for assets created from URL:'|t('feed-me'),
76 | name: 'options[filenameNode]',
77 | value: hash_get(feed.fieldMapping, optionsPath ~ '.filenameNode') ?: '',
78 | options: feedData|filter(option => option.value != 'usedefault'),
79 | class: 'selectize fullwidth',
80 | }) }}
81 |
82 |
83 | {% endblock %}
84 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/calendar-events.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% if field is defined %}
15 | {% set default = default ?? {
16 | type: 'elementselect',
17 | options: {
18 | elementType: fieldClass.elementType,
19 | selectionLabel: "Default Event"|t('feed-me'),
20 | sources: field.sources ?? '*',
21 | },
22 | } %}
23 | {% endif %}
24 |
25 | {% extends 'feed-me/_includes/fields/_base' %}
26 |
27 | {% block extraSettings %}
28 |
29 | {{ 'Data provided for this calendar event is:'|t('feed-me') }}
30 |
31 | {{ forms.selectField({
32 | name: 'options[match]',
33 | class: '',
34 | value: hash_get(feed.fieldMapping, optionsPath ~ '.match') ?: '',
35 | options: [
36 | { value: 'title', label: 'Title'|t('feed-me') },
37 | { value: 'id', label: 'ID'|t('feed-me') },
38 | { value: 'slug', label: 'Slug'|t('feed-me') },
39 | ],
40 | }) }}
41 |
42 | {% endblock %}
43 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/categories.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% if field is defined %}
15 | {% set default = default ?? {
16 | type: 'elementselect',
17 | options: {
18 | elementType: fieldClass.elementType,
19 | selectionLabel: "Default Category"|t('feed-me'),
20 | sources: field.source ? [field.source] : '*',
21 | },
22 | } %}
23 | {% endif %}
24 |
25 | {# Get any sub-fields for the element. Also check to go only one level deep #}
26 | {% if field is defined and isSubElementField is not defined %}
27 | {% set elementFields = [] %}
28 |
29 | {# Prevent infinite loop by only allow elements that are not the same as this #}
30 | {% for elementField in craft.feedme.getElementLayoutByField(className(field), field) %}
31 | {% if craft.feedme.supportedSubField(className(elementField)) %}
32 | {% set elementFields = elementFields|merge([ elementField ]) %}
33 | {% endif %}
34 | {% endfor %}
35 | {% endif %}
36 |
37 | {% extends 'feed-me/_includes/fields/_base' %}
38 |
39 | {% block extraSettings %}
40 |
41 | {{ 'Data provided for this category is:'|t('feed-me') }}
42 |
43 | {% set matchAttributes = [
44 | { value: 'title', label: 'Title'|t('feed-me') },
45 | { value: 'id', label: 'ID'|t('feed-me') },
46 | { value: 'slug', label: 'Slug'|t('feed-me') },
47 | ] %}
48 |
49 | {% if field is defined and field is not empty %}
50 | {% set matchAttributes = matchAttributes|merge(craft.feedme.getRelationFieldMatchOptions(className(field), feed, field)) %}
51 | {% else %}
52 | {% set matchAttributes = matchAttributes|merge(craft.feedme.getRelationFieldMatchOptions('craft\\fields\\Categories', feed, null)) %}
53 | {% endif %}
54 |
55 | {{ forms.selectField({
56 | name: 'options[match]',
57 | class: 'categories-field-match',
58 | value: hash_get(feed.fieldMapping, optionsPath ~ '.match') ?: '',
59 | options: matchAttributes,
60 | }) }}
61 |
62 |
63 | {# don't allow crating new categories if the source is custom, because we can't ensure all the conditions will be met;
64 | e.g. level, date created or updated etc #}
65 | {% if field is defined and field is not empty and field.source starts with 'custom:' == false %}
66 |
67 | {{ feedMeMacro.checkbox({
68 | label: 'Create categories if they do not exist'|t('feed-me'),
69 | name: 'options[create]',
70 | value: 1,
71 | checked: hash_get(feed.fieldMapping, optionsPath ~ '.create') ?: '',
72 | }) }}
73 |
74 | {% endif %}
75 | {% endblock %}
76 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/commerce_products.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% if field is defined %}
15 | {% set default = default ?? {
16 | type: 'elementselect',
17 | options: {
18 | elementType: fieldClass.elementType,
19 | selectionLabel: "Default Product"|t('feed-me'),
20 | sources: field.sources ?? '*',
21 | },
22 | } %}
23 | {% endif %}
24 |
25 | {% extends 'feed-me/_includes/fields/_base' %}
26 |
27 | {% block extraSettings %}
28 |
29 | {{ 'Data provided for this product is:'|t('feed-me') }}
30 |
31 | {{ forms.selectField({
32 | name: 'options[match]',
33 | class: '',
34 | value: hash_get(feed.fieldMapping, optionsPath ~ '.match') ?: '',
35 | options: [
36 | { value: 'title', label: 'Title'|t('feed-me') },
37 | { value: 'id', label: 'ID'|t('feed-me') },
38 | { value: 'slug', label: 'Slug'|t('feed-me') },
39 | ],
40 | }) }}
41 |
42 | {% endblock %}
43 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/commerce_variants.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% if field is defined %}
15 | {% set default = default ?? {
16 | type: 'elementselect',
17 | options: {
18 | elementType: fieldClass.elementType,
19 | selectionLabel: "Default Variant"|t('feed-me'),
20 | sources: field.sources ?? '*',
21 | },
22 | } %}
23 | {% endif %}
24 |
25 | {% extends 'feed-me/_includes/fields/_base' %}
26 |
27 | {% block extraSettings %}
28 |
29 | {{ 'Data provided for this variant is:'|t('feed-me') }}
30 |
31 | {{ forms.selectField({
32 | name: 'options[match]',
33 | class: '',
34 | value: hash_get(feed.fieldMapping, optionsPath ~ '.match') ?: '',
35 | options: [
36 | { value: 'title', label: 'Title'|t('feed-me') },
37 | { value: 'id', label: 'ID'|t('feed-me') },
38 | { value: 'sku', label: 'SKU'|t('feed-me') },
39 | ],
40 | }) }}
41 |
42 | {% endblock %}
43 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/country.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% if field is defined %}
15 | {% set options = [{
16 | 'label': ' ',
17 | 'value': '__blank__'
18 | }] %}
19 | {% set options = options|merge(craft.app.addresses.getCountryRepository().getList(craft.app.language)) %}
20 | {% set default = default ?? {
21 | type: 'select',
22 | options: options,
23 | } %}
24 | {% endif %}
25 |
26 | {% extends 'feed-me/_includes/fields/_base' %}
27 |
28 | {% block extraSettings %}
29 |
30 | {{ 'Data provided for this field is:'|t('feed-me') }}
31 |
32 | {{ forms.selectField({
33 | name: 'options[match]',
34 | class: '',
35 | options: [
36 | { value: 'value', label: 'Value'|t('feed-me') },
37 | { value: 'label', label: 'Label'|t('feed-me') }
38 | ],
39 | value: hash_get(feed.fieldMapping, optionsPath ~ '.match') ?: '',
40 | }) }}
41 |
42 | {% endblock %}
43 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/date.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% set default = default ?? {
15 | type: 'dateTime',
16 | } %}
17 |
18 | {% extends 'feed-me/_includes/fields/_base' %}
19 |
20 | {% block extraSettings %}
21 |
22 | {{ 'Data provided for this field is formatted as:'|t('feed-me') }}
23 |
24 | {{ forms.selectField({
25 | name: 'options[match]',
26 | class: '',
27 | value: hash_get(feed.fieldMapping, optionsPath ~ '.match') ?: '',
28 | options: [
29 | { value: 'auto', label: 'Auto'|t('feed-me') },
30 | { value: 'america', label: 'mm/dd/yyyy'|t('feed-me') },
31 | { value: 'america-short', label: 'mm/dd/yy'|t('feed-me') },
32 | { value: 'asia', label: 'yyyy/mm/dd'|t('feed-me') },
33 | { value: 'asia-short', label: 'yy/mm/dd'|t('feed-me') },
34 | { value: 'world', label: 'dd/mm/yyyy'|t('feed-me') },
35 | { value: 'world-short', label: 'dd/mm/yy'|t('feed-me') },
36 | { value: 'yyyymmdd', label: 'yyyymmdd'|t('feed-me') },
37 | { value: 'yymmdd', label: 'yymmdd'|t('feed-me') },
38 | { value: 'yyyyddmm', label: 'yyyyddmm'|t('feed-me') },
39 | { value: 'yyddmm', label: 'yyddmm'|t('feed-me') },
40 | { value: 'milliseconds', label: 'Timestamp (ms)'|t('feed-me') },
41 | { value: 'seconds', label: 'Timestamp (s)'|t('feed-me') },
42 | ],
43 | }) }}
44 |
45 | {% endblock %}
46 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/default.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% set default = default ?? {
15 | type: 'text',
16 | } %}
17 |
18 | {% extends 'feed-me/_includes/fields/_base' %}
19 |
20 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/digital-products.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% if field is defined %}
15 | {% set default = default ?? {
16 | type: 'elementselect',
17 | options: {
18 | elementType: fieldClass.elementType,
19 | selectionLabel: "Default Product"|t('feed-me'),
20 | sources: field.sources ?? '*',
21 | },
22 | } %}
23 | {% endif %}
24 |
25 | {% extends 'feed-me/_includes/fields/_base' %}
26 |
27 | {% block extraSettings %}
28 |
29 | {{ 'Data provided for this product is:'|t('feed-me') }}
30 |
31 | {{ forms.selectField({
32 | name: 'options[match]',
33 | class: '',
34 | value: hash_get(feed.fieldMapping, optionsPath ~ '.match') ?: '',
35 | options: [
36 | { value: 'title', label: 'Title'|t('feed-me') },
37 | { value: 'id', label: 'ID'|t('feed-me') },
38 | { value: 'slug', label: 'Slug'|t('feed-me') },
39 | ],
40 | }) }}
41 |
42 | {% endblock %}
43 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/element.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% extends 'feed-me/_includes/fields/_base' %}
15 |
16 | {% block extraSettings %}
17 |
18 | {{ 'Data provided for this element is:'|t('feed-me') }}
19 |
20 | {% set matchAttributes = [
21 | { value: 'title', label: 'Title'|t('feed-me') },
22 | { value: 'id', label: 'ID'|t('feed-me') },
23 | { value: 'slug', label: 'Slug'|t('feed-me') },
24 | ] %}
25 |
26 | {% for field in craft.app.fields.getAllFields() %}
27 | {% if craft.feedme.fieldCanBeUniqueId(field) %}
28 | {% set matchAttributes = matchAttributes|merge({ (field.handle): field.name }) %}
29 | {% endif %}
30 | {% endfor %}
31 |
32 | {{ forms.selectField({
33 | name: 'options[match]',
34 | class: '',
35 | value: hash_get(feed.fieldMapping, optionsPath ~ '.match') ?: '',
36 | options: matchAttributes,
37 | }) }}
38 |
39 | {% endblock %}
40 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/google-maps.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {# Special case when inside another complex field (Matrix) #}
15 | {% if parentPath is defined %}
16 | {% set prefixPath = parentPath %}
17 | {% else %}
18 | {% set prefixPath = [handle] %}
19 | {% endif %}
20 |
21 | {% set classes = ['complex-field'] %}
22 |
23 |
38 |
39 | {# Get comprehensive list of subfields, including coordinates #}
40 | {% set subfieldConfig = field.settings.subfieldConfig | merge([
41 | {
42 | 'handle': 'lat',
43 | 'label': 'Latitude'
44 | },
45 | {
46 | 'handle': 'lng',
47 | 'label': 'Longitude'
48 | },
49 | {
50 | 'handle': 'zoom',
51 | 'label': 'Zoom'
52 | },
53 | {
54 | 'handle': 'formatted',
55 | 'label': 'Formatted'
56 | },
57 | {
58 | 'handle': 'raw',
59 | 'label': 'Raw'
60 | }
61 | ]) %}
62 |
63 | {# Loop through all subfields #}
64 | {% for subfield in subfieldConfig %}
65 | {% set nameLabel = subfield['label'] %}
66 | {% set instructionsHandle = handle ~ '[' ~ subfield['handle'] ~ ']' %}
67 |
68 | {% set path = prefixPath|merge ([ 'fields', subfield['handle'] ]) %}
69 |
70 | {% set default = default ?? {
71 | type: 'text',
72 | } %}
73 |
74 | {% embed 'feed-me/_includes/fields/_base' %}
75 | {% block additionalFieldSettings %}
76 |
77 | {% endblock %}
78 |
79 | {% block fieldSettings %}
80 |
81 | {% endblock %}
82 | {% endembed %}
83 | {% endfor %}
84 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/icon.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% if field is defined %}
15 | {% set default = default ?? {
16 | type: 'iconpicker',
17 | name: "Default Icon"|t('feed-me'),
18 | } %}
19 | {% endif %}
20 |
21 | {% extends 'feed-me/_includes/fields/_base' %}
22 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/lightswitch.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% set default = default ?? {
15 | type: 'lightswitch',
16 | } %}
17 |
18 | {% extends 'feed-me/_includes/fields/_base' %}
19 |
20 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/linkit.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {# Special case when inside another complex field (Matrix) #}
15 | {% if parentPath is defined %}
16 | {% set prefixPath = parentPath %}
17 | {% else %}
18 | {% set prefixPath = [handle] %}
19 | {% endif %}
20 |
21 | {% set classes = ['complex-field'] %}
22 |
23 |
38 |
39 | {% set subfields = {
40 | type: 'Type',
41 | value: 'Value',
42 | customText: 'Custom Text',
43 | target: 'Target',
44 | } %}
45 |
46 | {% for key, col in subfields %}
47 | {% set nameLabel = col %}
48 | {% set instructionsHandle = handle ~ '[' ~ key ~ ']' %}
49 |
50 | {% set path = prefixPath|merge ([ 'fields', key ]) %}
51 |
52 | {% set default = {
53 | type: 'text'
54 | } %}
55 |
56 | {% switch key %}
57 |
58 | {% case 'type' %}
59 |
60 | {% set enabledLinkTypes = field.getEnabledLinkTypes() %}
61 | {% set enabledLinkTypeOptions = [
62 | { label: 'Don’t import'|t('feed-me'), value: '' }
63 | ] %}
64 |
65 | {% for enabledLinkType in enabledLinkTypes %}
66 | {% set enabledLinkTypeOptions = enabledLinkTypeOptions|merge([{ label: enabledLinkType.label, value: enabledLinkType.type }]) %}
67 | {% endfor %}
68 |
69 | {% set default = {
70 | type: 'select',
71 | options: enabledLinkTypeOptions,
72 | } %}
73 |
74 | {% case 'target' %}
75 |
76 | {% set default = {
77 | type: 'select',
78 | options: [
79 | { label: 'Don’t import'|t('feed-me'), value: '' },
80 | { label: 'Open in new window', value: '1' },
81 | { label: 'Open in existing window', value: '' },
82 | ]
83 | } %}
84 |
85 | {% endswitch %}
86 |
87 |
88 | {% embed 'feed-me/_includes/fields/_base' %}
89 | {% block additionalFieldSettings %}
90 |
91 | {% endblock %}
92 |
93 | {% block fieldSettings %}
94 |
95 | {% endblock %}
96 | {% endembed %}
97 | {% endfor %}
98 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/money.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% set default = default ?? {
15 | type: 'text',
16 | } %}
17 |
18 | {% extends 'feed-me/_includes/fields/_base' %}
19 |
20 | {% block extraSettings %}
21 |
22 | {{ forms.checkboxField({
23 | name: 'options[localized]',
24 | label: 'Data provided for this localized for the site the feed is for'|t('feed-me'),
25 | class: '',
26 | value: 1,
27 | checked: hash_get(feed.fieldMapping, optionsPath ~ '.localized') ? true : false
28 | }) }}
29 |
30 | {% endblock %}
31 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/option-select.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% if field is defined %}
15 | {% set default = default ?? {
16 | type: 'select',
17 | options: craft.feedme.getSelectOptions(field.settings.options, 'label', 'value'),
18 | } %}
19 | {% endif %}
20 |
21 | {% extends 'feed-me/_includes/fields/_base' %}
22 |
23 | {% block extraSettings %}
24 |
25 | {{ 'Data provided for this field is:'|t('feed-me') }}
26 |
27 | {{ forms.selectField({
28 | name: 'options[match]',
29 | class: '',
30 | options: [
31 | { value: 'value', label: 'Value'|t('feed-me') },
32 | { value: 'label', label: 'Label'|t('feed-me') }
33 | ],
34 | value: hash_get(feed.fieldMapping, optionsPath ~ '.match') ?: '',
35 | }) }}
36 |
37 | {% endblock %}
38 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/simple-map.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {# Special case when inside another complex field (Matrix) #}
15 | {% if parentPath is defined %}
16 | {% set prefixPath = parentPath %}
17 | {% else %}
18 | {% set prefixPath = [handle] %}
19 | {% endif %}
20 |
21 | {% set classes = ['complex-field'] %}
22 |
23 |
38 |
39 | {% set simpleMapSubfields = {
40 | lat: 'Latitude',
41 | lng: 'Longitude',
42 | zoom: 'Zoom',
43 | address: 'Address',
44 | } %}
45 |
46 | {% for key, col in simpleMapSubfields %}
47 | {% set nameLabel = col %}
48 | {% set instructionsHandle = handle ~ '[' ~ key ~ ']' %}
49 |
50 | {% set path = prefixPath|merge ([ 'fields', key ]) %}
51 |
52 | {% set default = default ?? {
53 | type: 'text',
54 | } %}
55 |
56 | {% embed 'feed-me/_includes/fields/_base' %}
57 | {% block additionalFieldSettings %}
58 |
59 | {% endblock %}
60 |
61 | {% block fieldSettings %}
62 |
63 | {% endblock %}
64 | {% endembed %}
65 | {% endfor %}
66 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/smart-map.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {# Special case when inside another complex field (Matrix) #}
15 | {% if parentPath is defined %}
16 | {% set prefixPath = parentPath %}
17 | {% else %}
18 | {% set prefixPath = [handle] %}
19 | {% endif %}
20 |
21 | {% set classes = ['complex-field'] %}
22 |
23 |
38 |
39 | {% for key, col in field.settings.layout %}
40 | {% set nameLabel = craft.smartMap.labelFromHandle(key) %}
41 | {% set instructionsHandle = handle ~ '[' ~ key ~ ']' %}
42 |
43 | {% set path = prefixPath|merge ([ 'fields', key ]) %}
44 |
45 | {% set default = default ?? {
46 | type: 'text',
47 | } %}
48 |
49 | {% embed 'feed-me/_includes/fields/_base' %}
50 | {% block additionalFieldSettings %}
51 |
52 | {% endblock %}
53 |
54 | {% block fieldSettings %}
55 |
56 | {% endblock %}
57 | {% endembed %}
58 | {% endfor %}
59 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/super-table.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {# Special case when inside another complex field (Matrix) #}
15 | {% if parentPath is defined %}
16 | {% set prefixPath = parentPath %}
17 | {% else %}
18 | {% set prefixPath = [handle] %}
19 | {% endif %}
20 |
21 | {% set classes = ['complex-field'] %}
22 |
23 |
24 | {% namespace 'fieldMapping[' ~ prefixPath|join('][') ~ ']' %}
25 |
26 |
27 | {% endnamespace %}
28 |
29 |
30 | {% for blocktype in field.blocktypes %}
31 | {% if blocktype.fields|length %}
32 |
47 |
48 | {% for blocktypefield in blocktype.getCustomFields() %}
49 | {% set nameLabel = blocktypefield.name %}
50 | {% set instructionsHandle = handle ~ '[' ~ blocktypefield.handle ~ ']' %}
51 |
52 | {% set parentPath = prefixPath|merge ([ 'fields', blocktypefield.handle ]) %}
53 |
54 | {% set fieldClass = craft.feedme.fields.getRegisteredField(className(blocktypefield)) %}
55 | {% set template = fieldClass.getMappingTemplate() %}
56 |
57 | {% include template ignore missing with {
58 | field: blocktypefield,
59 | handle: blocktypefield.handle,
60 | path: parentPath,
61 | } %}
62 | {% endfor %}
63 | {% endif %}
64 | {% endfor %}
65 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/table.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {# Special case when inside another complex field (Matrix) #}
15 | {% if parentPath is defined %}
16 | {% set prefixPath = parentPath %}
17 | {% else %}
18 | {% set prefixPath = [handle] %}
19 | {% endif %}
20 |
21 | {% set classes = ['complex-field'] %}
22 |
23 |
38 |
39 | {% set colTypeMappings = {
40 | 'checkbox': 'checkbox',
41 | 'color': 'text',
42 | 'date': 'date',
43 | 'select': 'select',
44 | 'email': 'text',
45 | 'lightswitch': 'lightswitch',
46 | 'multiline': 'text',
47 | 'number': 'text',
48 | 'singleline': 'text',
49 | 'time': 'time',
50 | 'url': 'text',
51 | } %}
52 |
53 | {% for index, col in field.settings.columns %}
54 | {% set nameLabel = col.heading %}
55 | {% set instructionsHandle = handle ~ '[' ~ col.handle ~ ']' %}
56 |
57 | {% set path = prefixPath|merge ([ 'fields', index ]) %}
58 |
59 | {% if colTypeMappings[col.type] is defined %}
60 | {% set default = {
61 | type: colTypeMappings[col.type],
62 | } %}
63 | {% if colTypeMappings[col.type] == 'select' %}
64 | {% set default = default|merge({options: col.options}) %}
65 | {% endif %}
66 | {% endif %}
67 |
68 | {% embed 'feed-me/_includes/fields/_base' %}
69 | {% block additionalFieldSettings %}
70 | {% namespace 'fieldMapping[' ~ path|join('][') ~ ']' %}
71 |
72 |
73 | {% endnamespace %}
74 | {% endblock %}
75 |
76 | {% block fieldSettings %}
77 |
78 | {% endblock %}
79 | {% endembed %}
80 | {% endfor %}
81 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/tags.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% if field is defined %}
15 | {% set default = default ?? {
16 | type: 'elementselect',
17 | options: {
18 | elementType: fieldClass.elementType,
19 | selectionLabel: "Default Tag"|t('feed-me'),
20 | sources: field.source ? [field.source] : '*',
21 | },
22 | } %}
23 | {% endif %}
24 |
25 | {# Get any sub-fields for the element. Also check to go only one level deep #}
26 | {% if field is defined and isSubElementField is not defined %}
27 | {% set elementFields = [] %}
28 |
29 | {# Prevent infinite loop by only allow elements that are not the same as this #}
30 | {% for elementField in craft.feedme.getElementLayoutByField(className(field), field) %}
31 | {% if craft.feedme.supportedSubField(className(elementField)) %}
32 | {% set elementFields = elementFields|merge([ elementField ]) %}
33 | {% endif %}
34 | {% endfor %}
35 | {% endif %}
36 |
37 | {% extends 'feed-me/_includes/fields/_base' %}
38 |
39 | {% block extraSettings %}
40 |
41 | {{ feedMeMacro.checkbox({
42 | label: 'Create tags if they do not exist'|t('feed-me'),
43 | name: 'options[create]',
44 | value: 1,
45 | checked: hash_get(feed.fieldMapping, optionsPath ~ '.create') ?: '',
46 | }) }}
47 |
48 | {% endblock %}
49 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/time.html:
--------------------------------------------------------------------------------
1 | {% import 'feed-me/_macros' as feedMeMacro %}
2 | {% import '_includes/forms' as forms %}
3 |
4 | {% set default = default ?? {
5 | type: 'time',
6 | } %}
7 |
8 | {% extends 'feed-me/_includes/fields/date' %}
9 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/typed-link.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {# Special case when inside another complex field (Matrix) #}
15 | {% if parentPath is defined %}
16 | {% set prefixPath = parentPath %}
17 | {% else %}
18 | {% set prefixPath = [handle] %}
19 | {% endif %}
20 |
21 | {% set classes = ['complex-field'] %}
22 |
23 |
38 |
39 | {% set subfields = {
40 | type: 'Type',
41 | value: 'Custom URL',
42 | customText: 'Custom Text',
43 | } %}
44 |
45 | {% for key, col in subfields %}
46 | {% set nameLabel = col %}
47 | {% set instructionsHandle = handle ~ '[' ~ key ~ ']' %}
48 |
49 | {% set path = prefixPath|merge ([ 'fields', key ]) %}
50 |
51 | {% set default = default ?? {
52 | type: 'text',
53 | } %}
54 |
55 | {% embed 'feed-me/_includes/fields/_base' %}
56 | {% block additionalFieldSettings %}
57 |
58 | {% endblock %}
59 |
60 | {% block fieldSettings %}
61 |
62 | {% endblock %}
63 | {% endembed %}
64 | {% endfor %}
65 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/userGroups.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% set default = default ?? {
15 | type: 'text',
16 | } %}
17 |
18 | {% extends 'feed-me/_includes/fields/_base' %}
19 |
20 | {% block extraSettings %}
21 |
22 | {{ feedMeMacro.checkbox({
23 | label: 'Assign user to only these groups (remove from any existing).'|t('feed-me'),
24 | name: 'options[removeFromExisting]',
25 | value: 1,
26 | checked: hash_get(feed.fieldMapping, optionsPath ~ '.removeFromExisting') ?: '',
27 | }) }}
28 |
29 | {% endblock %}
30 |
--------------------------------------------------------------------------------
/src/templates/_includes/fields/users.html:
--------------------------------------------------------------------------------
1 | {# ------------------------ #}
2 | {# Available Variables #}
3 | {# ------------------------ #}
4 | {# Attributes: #}
5 | {# type, name, handle, instructions, attribute, default, feed, feedData #}
6 | {# ------------------------ #}
7 | {# Fields: #}
8 | {# name, handle, instructions, feed, feedData, field, fieldClass #}
9 | {# ------------------------ #}
10 |
11 | {% import 'feed-me/_macros' as feedMeMacro %}
12 | {% import '_includes/forms' as forms %}
13 |
14 | {% if field is defined %}
15 | {% set default = default ?? {
16 | type: 'elementselect',
17 | options: {
18 | elementType: fieldClass.elementType,
19 | selectionLabel: "Default User"|t('feed-me'),
20 | sources: field.sources ?? '*',
21 | },
22 | } %}
23 | {% endif %}
24 |
25 | {# Get any sub-fields for the element. Also check to go only one level deep #}
26 | {% if field is defined and isSubElementField is not defined %}
27 | {% set elementFields = [] %}
28 |
29 | {# Prevent infinite loop by only allow elements that are not the same as this #}
30 | {% for elementField in craft.feedme.getUserLayoutByField() %}
31 | {% if craft.feedme.supportedSubField(className(elementField)) %}
32 | {% set elementFields = elementFields|merge([ elementField ]) %}
33 | {% endif %}
34 | {% endfor %}
35 | {% endif %}
36 |
37 | {% extends 'feed-me/_includes/fields/_base' %}
38 |
39 | {% block extraSettings %}
40 |
41 | {{ 'Data provided for this user is:'|t('feed-me') }}
42 |
43 | {% set matchAttributes = [
44 | { value: 'email', label: 'Email'|t('feed-me') },
45 | { value: 'id', label: 'ID'|t('feed-me') },
46 | { value: 'username', label: 'Username'|t('feed-me') },
47 | { value: 'fullName', label: 'Full Name'|t('feed-me') },
48 | ] %}
49 |
50 | {% if field is defined and field is not empty %}
51 | {% set matchAttributes = matchAttributes|merge(craft.feedme.getRelationFieldMatchOptions(className(field), feed, field)) %}
52 | {% else %}
53 | {% set matchAttributes = matchAttributes|merge(craft.feedme.getRelationFieldMatchOptions('craft\\fields\\Users', feed, null)) %}
54 | {% endif %}
55 |
56 | {{ forms.selectField({
57 | name: 'options[match]',
58 | class: 'users-field-match',
59 | value: hash_get(feed.fieldMapping, optionsPath ~ '.match') ?: '',
60 | options: matchAttributes,
61 | }) }}
62 |
63 |
64 | {# don't allow crating new entries if the only selected source is custom, because we can't ensure all the conditions will be met #}
65 | {% if field is defined and craft.feedme.fieldHasOnlyCustomSources(field) == false %}
66 |
67 | {{ feedMeMacro.checkbox({
68 | label: 'Create users if they do not exist'|t('feed-me'),
69 | name: 'options[create]',
70 | value: 1,
71 | checked: hash_get(feed.fieldMapping, optionsPath ~ '.create') ?: '',
72 | }) }}
73 |
74 | {% endif %}
75 | {% endblock %}
76 |
--------------------------------------------------------------------------------
/src/templates/_layouts/index.html:
--------------------------------------------------------------------------------
1 | {% extends parentLayout|default('_layouts/cp') %}
2 |
3 | {% do view.registerAssetBundle('craft\\feedme\\web\\assets\\feedme\\FeedMeAsset') %}
4 | {% set baseAssetsUrl = view.getAssetManager().getPublishedUrl('@craft/feedme/web/assets/feedme/dist', true) %}
5 |
6 | {% if noTabs is not defined %}
7 | {% set tabs = craft.feedme.getTabs() %}
8 | {% endif %}
9 |
10 | {% if title is not defined %}
11 | {% set title = craft.feedme.getPluginName ~ ' ' %}
12 | {% endif %}
13 |
14 | {% block content %}
15 | {% if block('footerButton') is defined %}
16 |
17 |
18 |
23 | {% endif %}
24 | {% endblock %}
25 |
--------------------------------------------------------------------------------
/src/templates/_layouts/settings.html:
--------------------------------------------------------------------------------
1 | {% extends 'feed-me/_layouts' %}
2 |
3 | {% set crumbs = [
4 | { label: craft.feedme.getPluginName|t('feed-me'), url: url('feed-me') },
5 | { label: "Settings"|t('feed-me'), url: url('feed-me/settings') },
6 | ] %}
7 |
8 | {% set selectedTab = 'settings' %}
9 | {% set selectedItem = craft.app.request.getSegment(3) %}
10 |
11 | {% set navItems = {
12 | 'general-heading': { heading: "Settings"|t('feed-me') },
13 | 'general': { title: "General"|t('feed-me') },
14 | } %}
15 |
16 | {% block sidebar %}
17 |
18 |
19 | {% for id, item in navItems %}
20 | {% if item.heading is defined %}
21 | {{ item.heading }}
22 | {% else %}
23 | {{ item.title }}
24 | {% endif %}
25 | {% endfor %}
26 |
27 |
28 | {% endblock %}
29 |
--------------------------------------------------------------------------------
/src/templates/_macros/index.html:
--------------------------------------------------------------------------------
1 | {% macro checkbox(config) %}
2 | {%- apply spaceless %}
3 |
4 | {% set value = (config.value is defined ? config.value : 1) %}
5 | {% set id = (config.id is defined and config.id ? config.id : 'checkbox' ~ random()) %}
6 | {% set label = (config.label is defined ? config.label) %}
7 |
8 |
9 |
14 |
15 | {{ label|raw }}
16 |
17 |
18 | {% endapply -%}
19 | {% endmacro %}
20 |
--------------------------------------------------------------------------------
/src/templates/_svg/verbb-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
17 |
18 |
--------------------------------------------------------------------------------
/src/templates/feeds/_direct.html:
--------------------------------------------------------------------------------
1 | {% do view.registerAssetBundle('craft\\feedme\\web\\assets\\feedme\\FeedMeAsset') %}
2 | {% set baseAssetsUrl = view.getAssetManager().getPublishedUrl('@craft/feedme/web/assets/feedme/dist', true) %}
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{ task ? 'Feed Me - {name} has started processing'|t('feed-me', { name: feed.name }) : 'Unable to start feed'|t('feed-me') }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | {% if task %}
19 |
20 |
21 |
22 |
{{ '{name} has started processing'|t('feed-me', { name: feed.name }) }}
23 |
{{ 'Data is now being imported from your feed. You can navigate away from this page without disrupting your feed processing.'|t('feed-me') }}
24 |
25 | {% else %}
26 |
27 |
28 |
29 |
{{ 'Unable to start feed'|t('feed-me') }}
30 |
{{ 'Please check your passkey, and verify the URL is correct.'|t('feed-me') }}
31 |
32 | {% endif %}
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/templates/feeds/_map.html:
--------------------------------------------------------------------------------
1 | {% extends 'feed-me/_layouts' %}
2 |
3 | {% set crumbs = [
4 | { label: craft.feedme.getPluginName|t('feed-me'), url: url('feed-me') },
5 | { label: feed.name|t('feed-me'), url: url('feed-me/feeds/' ~ feed.id) },
6 | { label: 'Element'|t('feed-me'), url: url('feed-me/feeds/element/' ~ feed.id) },
7 | { label: 'Map'|t('feed-me'), url: url('feed-me/feeds/map/' ~ feed.id) },
8 | ] %}
9 |
10 | {% set title = feed.name %}
11 |
12 | {% set title = (feed.id) ? feed.name : 'Create a new feed'|t('feed-me') %}
13 | {% set noTabs = true %}
14 | {% set fullPageForm = true %}
15 |
16 | {% set buttons %}
17 | {% if feedMappingData.success %}
18 |
19 |
20 |
21 |
22 | {% endif %}
23 | {% endset %}
24 |
25 | {% block actionButton %}
26 | {{ buttons }}
27 | {% endblock %}
28 |
29 | {% block content %}
30 | {% if feedMappingData.success %}
31 |
32 |
33 | {% if feed.id %}
34 |
35 | {% endif %}
36 |
37 | {% set parsedFeedData = [] %}
38 |
39 | {% for key, data in feedMappingData.data %}
40 | {% if data is iterable %}
41 | {% set snippet = '' %}
42 | {% else %}
43 | {% set snippet = data|length > 30 ? data[0:30] ~ '...' : data %}
44 | {% endif %}
45 |
46 | {% set parsedFeedData = parsedFeedData|merge([{ label: '<' ~ key ~ '> eg: ' ~ snippet, value: key }]) %}
47 | {% endfor %}
48 |
49 | {% set parsedFeedData = parsedFeedData|sort((a, b) => b.label < a.label) %}
50 |
51 | {% set parsedFeedData = [
52 | { label: 'Don’t import'|t('feed-me'), value: 'noimport' },
53 | { label: 'Use default value', value: 'usedefault' }
54 | ]|merge(parsedFeedData) %}
55 |
56 | {% include feed.getElement().getMappingTemplate() with { feedData: parsedFeedData } %}
57 |
58 | {% else %}
59 |
60 |
61 |
62 |
{{ 'Unable to proceed to field mapping'|t('feed-me') }}
63 |
{{ 'Feed Me is unable to find, or parse your provided data. This usually means your URL cannot be reached from your Craft site, or your {feedType} is invalid. Check the logs, and double-check your settings.'|t('feed-me', { feedType: feed.feedType|upper }) }}
64 |
65 | {% if feedMappingData.error is not empty %}
66 |
67 | {{ feedMappingData.error }}
68 |
69 | {% endif %}
70 |
71 |
75 |
76 | {% endif %}
77 |
78 | {{ parent() }}
79 | {% endblock %}
80 |
81 | {% block footerButton %}
82 | {{ buttons }}
83 | {% endblock %}
84 |
--------------------------------------------------------------------------------
/src/templates/feeds/_status.html:
--------------------------------------------------------------------------------
1 | {% extends 'feed-me/_layouts' %}
2 |
3 | {% set crumbs = [
4 | { label: craft.feedme.getPluginName|t('feed-me'), url: url('feed-me') },
5 | { label: feed.name|t('feed-me'), url: url('feed-me/feeds/' ~ feed.id) },
6 | ] %}
7 |
8 | {% set title = feed.name %}
9 |
10 | {% set title = (feed.id) ? feed.name : 'Create a new feed'|t('feed-me') %}
11 | {% set noTabs = true %}
12 |
13 | {% block content %}
14 |
15 |
16 |
17 |
{{ '{name} has been saved.'|t('feed-me', { name: feed.name }) }}
18 |
19 |
20 | {% apply spaceless %}
21 | {{ tag('a', {
22 | class: ['btn', 'submit'],
23 | href: url('feed-me/feeds/run/' ~ feed.id),
24 | text: 'Process it now'|t('feed-me'),
25 | }) }}
26 | {{ tag('a', {
27 | class: ['btn'],
28 | href: url('feed-me/feeds'),
29 | text: 'View feeds'|t('feed-me'),
30 | }) }}
31 | {% endapply %}
32 |
33 |
34 |
35 | {{ parent() }}
36 | {% endblock %}
37 |
--------------------------------------------------------------------------------
/src/templates/index.html:
--------------------------------------------------------------------------------
1 | {% redirect 'feed-me/feeds' %}
--------------------------------------------------------------------------------
/src/templates/settings/general.html:
--------------------------------------------------------------------------------
1 | {% requireAdmin false %}
2 |
3 | {% extends 'feed-me/_layouts/settings' %}
4 |
5 | {% import '_includes/forms' as forms %}
6 |
7 | {% set readOnly = readOnly ?? false %}
8 | {% set fullPageForm = not readOnly %}
9 |
10 | {% if readOnly %}
11 | {% set contentNotice = readOnlyNotice() %}
12 | {% endif %}
13 |
14 | {% block content %}
15 |
16 |
17 |
18 | {% namespace 'settings' %}
19 |
20 | {{ forms.textField({
21 | id: 'pluginName',
22 | name: 'pluginName',
23 | label: 'Plugin Name'|t('feed-me'),
24 | value: settings.pluginName,
25 | first: true,
26 | autofocus: true,
27 | instructions: 'Plugin name for the end user.'|t('feed-me'),
28 | disabled: readOnly,
29 | }) }}
30 |
31 | {{ forms.textField({
32 | id: 'cache',
33 | name: 'cache',
34 | label: 'Cache Duration'|t('feed-me'),
35 | value: settings.cache,
36 | instructions: 'Cache duration (in seconds) to cache requests. Note: this only affects calls using the template tag - requests are never cached when triggering directly via the CP.'|t('feed-me'),
37 | disabled: readOnly,
38 | }) }}
39 |
40 |
41 |
42 | {{ forms.checkboxSelectField({
43 | id: 'enabledTabs',
44 | name: 'enabledTabs',
45 | label: 'Enabled Tabs'|t('feed-me'),
46 | showAllOption: true,
47 | options: {
48 | 'feeds': 'Feeds'|t('feed-me'),
49 | 'logs': 'Logs'|t('feed-me'),
50 | 'utilities': 'Utilities'|t('feed-me'),
51 | 'settings': 'Settings'|t('feed-me'),
52 | },
53 | values: settings.enabledTabs,
54 | instructions: 'Choose which tabs you would like to be shown.'|t('feed-me'),
55 | disabled: readOnly,
56 | }) }}
57 |
58 | {% endnamespace %}
59 |
60 | {{ parent() }}
61 | {% endblock %}
62 |
--------------------------------------------------------------------------------
/src/templates/settings/index.html:
--------------------------------------------------------------------------------
1 | {% redirect 'feed-me/settings/general' %}
--------------------------------------------------------------------------------
/src/templates/utilities/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'feed-me/_layouts' %}
2 |
3 | {% set crumbs = [
4 | { label: craft.feedme.getPluginName|t('feed-me'), url: url('feed-me') },
5 | { label: "Utilities"|t('feed-me'), url: url('feed-me/utilities') }
6 | ] %}
7 |
8 | {% set selectedTab = 'utilities' %}
9 |
10 | {% block content %}
11 |
12 | {{ "Clear pending job queue"|t('feed-me') }}
13 |
14 | {% endblock %}
15 |
--------------------------------------------------------------------------------
/src/templates/welcome/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'feed-me/_layouts' %}
2 |
3 | {% set noTabs = true %}
4 |
5 | {% set title = 'Welcome to {name}!'|t('feed-me', { name: craft.feedme.getPluginName() }) %}
6 |
7 | {% block content %}
8 |
9 |
10 | {{ svg(craft.app.plugins.getPluginIconSvg('feed-me'))|attr({
11 | style: {
12 | display: 'inline-block',
13 | },
14 | }) }}
15 |
16 |
17 |
{{ "Thanks for using {name}!"|t('feed-me', { name: craft.feedme.getPluginName() }) }}
18 |
19 |
{{ "Thanks for installing {name}. Get started by creating your first feed."|t('feed-me', { name: craft.feedme.getPluginName() }) }}
20 |
21 |
{{ "Get Started"|t('feed-me') }}
22 |
{{ "or"|t('feed-me') }}
23 |
{{ "Read our Online Documentation"|t('feed-me') }}
24 |
25 |
26 | {{ parent() }}
27 | {% endblock %}
28 |
--------------------------------------------------------------------------------
/src/web/assets/feedme/FeedMeAsset.php:
--------------------------------------------------------------------------------
1 | sourcePath = "@craft/feedme/web/assets/feedme/dist";
23 |
24 | $this->depends = [
25 | CpAsset::class,
26 | ];
27 |
28 | $this->js = [
29 | 'FeedMe.js',
30 | ];
31 |
32 | $this->css = [
33 | 'css/FeedMe.css',
34 | ];
35 |
36 | parent::init();
37 | }
38 |
39 | /**
40 | * @inheritdoc
41 | */
42 | public function registerAssetFiles($view): void
43 | {
44 | parent::registerAssetFiles($view);
45 |
46 | $elementTypeInfo = [];
47 | foreach (Plugin::getInstance()->getElements()->getRegisteredElements() as $elementClass => $element) {
48 | $groups = [];
49 | $elementGroups = $element->getGroups();
50 | foreach ($elementGroups as $group) {
51 | if ($group instanceof ElementGroup) {
52 | $groups[$group->id] = [
53 | 'isSingleton' => $group->isSingleton,
54 | ];
55 | }
56 | }
57 | $elementTypeInfo[$elementClass] = [
58 | 'groups' => $groups,
59 | ];
60 | }
61 |
62 | $json = Json::encode($elementTypeInfo, JSON_UNESCAPED_UNICODE);
63 | $js = <<registerJs($js, View::POS_HEAD);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/web/assets/feedme/dist/fonts/fa-regular-400.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/dist/fonts/fa-regular-400.eot
--------------------------------------------------------------------------------
/src/web/assets/feedme/dist/fonts/fa-regular-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/dist/fonts/fa-regular-400.ttf
--------------------------------------------------------------------------------
/src/web/assets/feedme/dist/fonts/fa-regular-400.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/dist/fonts/fa-regular-400.woff
--------------------------------------------------------------------------------
/src/web/assets/feedme/dist/fonts/fa-regular-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/dist/fonts/fa-regular-400.woff2
--------------------------------------------------------------------------------
/src/web/assets/feedme/dist/fonts/fa-solid-900.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/dist/fonts/fa-solid-900.eot
--------------------------------------------------------------------------------
/src/web/assets/feedme/dist/fonts/fa-solid-900.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/dist/fonts/fa-solid-900.ttf
--------------------------------------------------------------------------------
/src/web/assets/feedme/dist/fonts/fa-solid-900.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/dist/fonts/fa-solid-900.woff
--------------------------------------------------------------------------------
/src/web/assets/feedme/dist/fonts/fa-solid-900.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/dist/fonts/fa-solid-900.woff2
--------------------------------------------------------------------------------
/src/web/assets/feedme/dist/img/icon-error.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/web/assets/feedme/dist/img/icon-success.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/web/assets/feedme/src/FeedMe.js:
--------------------------------------------------------------------------------
1 | // Import CSS
2 | import './scss/feed-me.scss';
3 |
4 | // Import JS
5 | import './lib/selectize.js';
6 | import './js/feed-me.js';
7 |
--------------------------------------------------------------------------------
/src/web/assets/feedme/src/fonts/fa-regular-400.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/src/fonts/fa-regular-400.eot
--------------------------------------------------------------------------------
/src/web/assets/feedme/src/fonts/fa-regular-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/src/fonts/fa-regular-400.ttf
--------------------------------------------------------------------------------
/src/web/assets/feedme/src/fonts/fa-regular-400.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/src/fonts/fa-regular-400.woff
--------------------------------------------------------------------------------
/src/web/assets/feedme/src/fonts/fa-regular-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/src/fonts/fa-regular-400.woff2
--------------------------------------------------------------------------------
/src/web/assets/feedme/src/fonts/fa-solid-900.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/src/fonts/fa-solid-900.eot
--------------------------------------------------------------------------------
/src/web/assets/feedme/src/fonts/fa-solid-900.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/src/fonts/fa-solid-900.ttf
--------------------------------------------------------------------------------
/src/web/assets/feedme/src/fonts/fa-solid-900.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/src/fonts/fa-solid-900.woff
--------------------------------------------------------------------------------
/src/web/assets/feedme/src/fonts/fa-solid-900.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftcms/feed-me/f41cdd9b77a2531a1b74961d05b214b45ce56bf9/src/web/assets/feedme/src/fonts/fa-solid-900.woff2
--------------------------------------------------------------------------------
/src/web/assets/feedme/src/img/icon-error.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/web/assets/feedme/src/img/icon-success.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/web/assets/feedme/webpack.config.js:
--------------------------------------------------------------------------------
1 | /* jshint esversion: 6 */
2 | /* globals module, require */
3 | const {getConfig} = require('@craftcms/webpack');
4 | const CopyWebpackPlugin = require('copy-webpack-plugin');
5 |
6 | module.exports = getConfig({
7 | context: __dirname,
8 | config: {
9 | entry: {
10 | FeedMe: './FeedMe.js',
11 | },
12 | plugins: [
13 | new CopyWebpackPlugin({
14 | patterns: [
15 | {
16 | from: './img/**/*',
17 | to: '.',
18 | },
19 | ],
20 | }),
21 | ],
22 | },
23 | });
24 |
--------------------------------------------------------------------------------
/src/web/twig/Extension.php:
--------------------------------------------------------------------------------
1 |