├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── bug_report.yaml │ ├── config.yml │ ├── feature_request.yaml │ └── support_request.yaml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config.codekit3 ├── docs ├── .sidebar.json ├── README.md ├── feature-tour │ └── usage.md └── get-started │ ├── installation-setup.md │ └── requirements.md └── src ├── ManyToMany.php ├── assetbundles └── field │ ├── ManyToManyFieldAsset.php │ ├── dist │ └── many-to-many.js │ └── src │ └── many-to-many.js ├── base └── PluginTrait.php ├── fields └── ManyToManyField.php ├── icon-mask.svg ├── icon.svg ├── services └── Service.php ├── templates └── field │ ├── input.html │ └── settings.html └── translations └── en └── manytomany.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: verbb 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a report to help us improve. 3 | body: 4 | - type: markdown 5 | attributes: 6 | value: | 7 | Before you send through your bug report, please ensure you have taken these steps first: 8 | 9 | ✅ I‘ve searched open and closed issues. 10 | 11 | - type: textarea 12 | id: bug-description 13 | attributes: 14 | label: Describe the bug 15 | description: Describe the bug and what behaviour you expect if the bug is fixed. 16 | placeholder: "I have an issue where..." 17 | validations: 18 | required: true 19 | - type: textarea 20 | id: steps-to-reproduce 21 | attributes: 22 | label: Steps to reproduce 23 | description: Detail how we can reproduce this issue. 24 | value: | 25 | 1. 26 | 2. 27 | validations: 28 | required: true 29 | - type: input 30 | id: craft-version 31 | attributes: 32 | label: Craft CMS version 33 | description: What version of Craft CMS you‘re using. **Do not write "latest"**. 34 | validations: 35 | required: true 36 | - type: input 37 | id: plugin-version 38 | attributes: 39 | label: Plugin version 40 | description: What version of the plugin you‘re using. **Do not write "latest"**. 41 | validations: 42 | required: true 43 | - type: input 44 | id: multi-site 45 | attributes: 46 | label: Multi-site? 47 | description: Whether your install is a multi-site. 48 | placeholder: | 49 | "Yes" or "No" 50 | - type: textarea 51 | id: additional-context 52 | attributes: 53 | label: Additional context 54 | description: Provide any additional information you think might be useful. The more information you provide the easier it‘ll be for use to fix this bug! 55 | placeholder: | 56 | "I also have X plugin installed..." or "This only happens on production..." -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Craft Discord 4 | url: https://craftcms.com/discord 5 | about: Community discussion and support. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea or enhancement. 3 | labels: 'feature request' 4 | body: 5 | - type: textarea 6 | id: feature-description 7 | attributes: 8 | label: What are you trying to do? 9 | description: A description of what you want to happen. 10 | placeholder: "I would like to see..." 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: proposed-solution 15 | attributes: 16 | label: What's your proposed solution? 17 | description: A description of how you think this could be solved, including any alternatives that you considered. 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: additional-context 22 | attributes: 23 | label: Additional context 24 | description: Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/support_request.yaml: -------------------------------------------------------------------------------- 1 | name: Question 2 | description: Ask a question about this plugin. DO NOT use this to submit bug reports. 3 | labels: 'question' 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Before you send through your question, please ensure you have taken these steps first: 9 | 10 | ✅ I‘ve searched open and closed issues. 11 | ✅ This is not a bug report, just a general question. 12 | 13 | - type: textarea 14 | id: question 15 | attributes: 16 | label: Question 17 | description: A question about the plugin or how it works. 18 | placeholder: "Is it possible to do..." 19 | validations: 20 | required: true 21 | - type: textarea 22 | id: additional-context 23 | attributes: 24 | label: Additional context 25 | description: Add any other context or screenshots about your question here. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # CRAFT ENVIRONMENT 2 | .env.php 3 | .env.sh 4 | .env 5 | 6 | # COMPOSER 7 | /vendor 8 | 9 | # BUILD FILES 10 | /bower_components/* 11 | /node_modules/* 12 | /build/* 13 | /yarn-error.log 14 | 15 | # MISC FILES 16 | .cache 17 | .DS_Store 18 | .idea 19 | .project 20 | .settings 21 | .map 22 | *.esproj 23 | *.sublime-workspace 24 | *.sublime-project 25 | *.tmproj 26 | *.tmproject 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 4.0.2 - 2025-02-05 4 | 5 | ### Fixed 6 | - Craft 5 updates and fixes. 7 | 8 | ## 4.0.1 - 2024-09-07 9 | 10 | ### Added 11 | - Add `valueType()` to field. (thanks @markhuot). 12 | 13 | ### Fixed 14 | - Fix `getTableAttributeHtml` not using default CP rendering. (thanks @regularlabs). 15 | 16 | ## 4.0.0 - 2024-05-13 17 | 18 | ### Changed 19 | - Now requires PHP `8.2.0+`. 20 | - Now requires Craft `5.0.0+`. 21 | 22 | ## 3.0.5 - 2024-09-07 23 | 24 | ### Added 25 | - Add `valueType()` to field. (thanks @markhuot). 26 | 27 | ### Fixed 28 | - Fix `getTableAttributeHtml` not using default CP rendering. (thanks @regularlabs). 29 | 30 | ## 3.0.4 - 2023-10-05 31 | 32 | ### Fixed 33 | - Fix GraphQL queries not allowing arguments on field. 34 | 35 | ## 3.0.3 - 2023-09-14 36 | 37 | ### Fixed 38 | - Fix inverse relations not working. 39 | 40 | ## 3.0.2 - 2022-12-06 41 | 42 | ### Fixed 43 | - Migrate field settings to uid instead of ids. 44 | 45 | ## 3.0.1 - 2022-11-30 46 | 47 | ### Fixed 48 | - Fix fields not migrating to new package name correctly. 49 | 50 | ## 3.0.0 - 2022-11-24 51 | 52 | ### Changed 53 | - Now requires PHP `8.0.2+`. 54 | - Now requires Craft `4.0.0+`. 55 | 56 | ## 2.0.2 - 2022-12-06 57 | 58 | ### Fixed 59 | - Migrate field settings to uid instead of ids. 60 | 61 | ## 2.0.1 - 2022-11-30 62 | 63 | ### Fixed 64 | - Fix fields not migrating to new package name correctly. 65 | 66 | ## 2.0.0 - 2022-11-24 67 | 68 | > {note} The plugin’s package name has changed to `verbb/many-to-many`. Many To Many will need be updated to 2.0 from a terminal, by running `composer require verbb/many-to-many && composer remove page-8/craft-manytomany`. 69 | 70 | ### Added 71 | - Add GraphQL support for querying the field. 72 | - Add “Selection Label” to field settings. 73 | - Add preview in CP content index table. (thanks @svale). 74 | 75 | ### Changed 76 | - Migration to `verbb/many-to-many`. 77 | - Now requires Craft 3.7+. 78 | - Update input.js to use boolean value. (thanks @matthisamoto). 79 | - Refactor field to use `normalizeValue` so it has a proper value for front-end, control panel, element index, and GraphQL. 80 | 81 | ## 1.0.2.2 - 2018-05-17 82 | 83 | ### Fixed 84 | - Injected Javascript HTML should reflect updated template HTML. 85 | 86 | ## 1.0.2.1 - 2018-05-15 87 | 88 | ### Added 89 | - Update input template to better display elements. 90 | 91 | ## 0.1.2 - 2016-06-15 92 | 93 | ### Added 94 | - Translatable text. 95 | 96 | ## 0.1.1 97 | 98 | ### Added 99 | - Optimized the cache control. Instead of clearing all Entry types from the cache, just clears records related to the changed element. 100 | 101 | ## 0.1.0 102 | 103 | ### Added 104 | - Initial Release. 105 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Verbb, 2014 Page 8 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Many to Many icon

2 |

Many to Many for Craft CMS

3 | 4 | Many to Many is a Craft CMS plugin to enable two-way relationships from either of the entries that belong to the association. For example, if you have a recipe with many ingredients, and ingredients that belong to many recipes, you can manage the relationship from either the Recipe's entry or the Ingredient's entry. 5 | 6 | ## Documentation 7 | Visit the [Many to Many Plugin page](https://verbb.io/craft-plugins/many-to-many) for all documentation, guides, pricing and developer resources. 8 | 9 | ## Credit & Thanks 10 | Originally created by [Oberon](https://www.oberon.nl) and [page.works](https://www.page.works). 11 | 12 | ## Support 13 | Get in touch with us via the [Many to Many Support page](https://verbb.io/craft-plugins/many-to-many/support) or by [creating a Github issue](https://github.com/verbb/many-to-many/issues) 14 | 15 | ## Sponsor 16 | Many to Many is licensed under the MIT license, meaning it will always be free and open source – we love free stuff! If you'd like to show your support to the plugin regardless, [Sponsor](https://github.com/sponsors/verbb) development. 17 | 18 |

19 | 20 | 21 | Verbb 22 | 23 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verbb/many-to-many", 3 | "description": "A field type to manage relationships from both sides.", 4 | "type": "craft-plugin", 5 | "version": "4.0.2", 6 | "keywords": [ 7 | "craft", 8 | "cms", 9 | "craftcms", 10 | "craft-plugin", 11 | "fieldtype", 12 | "entries" 13 | ], 14 | "support": { 15 | "email": "support@verbb.io", 16 | "issues": "https://github.com/verbb/many-to-many/issues?state=open", 17 | "source": "https://github.com/verbb/many-to-many", 18 | "docs": "https://github.com/verbb/many-to-many", 19 | "rss": "https://github.com/verbb/many-to-many/commits/v2.atom" 20 | }, 21 | "license": "MIT", 22 | "authors": [ 23 | { 24 | "name": "Verbb", 25 | "homepage": "https://verbb.io" 26 | }, 27 | { 28 | "name": "Page 8", 29 | "homepage": "http://page-8.com/" 30 | }, 31 | { 32 | "name": "Oberon Amsterdam", 33 | "homepage": "https://www.oberon.nl" 34 | } 35 | ], 36 | "require": { 37 | "craftcms/cms": "^5.0.0", 38 | "verbb/base": "^3.0.0" 39 | }, 40 | "autoload": { 41 | "psr-4": { 42 | "verbb\\manytomany\\": "src/" 43 | } 44 | }, 45 | "extra": { 46 | "name": "Many to Many", 47 | "handle": "manytomany", 48 | "changelogUrl": "https://raw.githubusercontent.com/verbb/many-to-many/craft-5/CHANGELOG.md", 49 | "class": "verbb\\manytomany\\ManyToMany" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /config.codekit3: -------------------------------------------------------------------------------- 1 | { 2 | "AAInfo" : "This is a CodeKit 3 project config file. EDITING THIS FILE IS A POOR LIFE DECISION. Doing so may cause CodeKit to crash and\/or corrupt your project. Several critical values in this file are 64-bit integers, which JavaScript JSON parsers do not support because JavaScript cannot handle 64-bit integers. These values will be corrupted if the file is parsed with JavaScript. This file is not backwards-compatible with CodeKit 1 or 2. For details, see https:\/\/codekitapp.com\/", 3 | "buildSteps" : [ 4 | { 5 | "name" : "Process All Remaining Files and Folders", 6 | "stepType" : 1, 7 | "uuidString" : "1B985AEF-BD59-4457-8269-EEBFCD342117" 8 | } 9 | ], 10 | "creatorBuild" : "34518", 11 | "files" : { 12 | "\/.github\/FUNDING.yml" : { 13 | "cB" : 0, 14 | "ft" : 8192, 15 | "hM" : 0, 16 | "oA" : 0, 17 | "oAP" : "\/.github\/FUNDING.yml", 18 | "oF" : 0 19 | }, 20 | "\/.github\/ISSUE_TEMPLATE\/bug_report.yaml" : { 21 | "cB" : 0, 22 | "ft" : 8192, 23 | "hM" : 0, 24 | "oA" : 0, 25 | "oAP" : "\/.github\/ISSUE_TEMPLATE\/bug_report.yaml", 26 | "oF" : 0 27 | }, 28 | "\/.github\/ISSUE_TEMPLATE\/config.yml" : { 29 | "cB" : 0, 30 | "ft" : 8192, 31 | "hM" : 0, 32 | "oA" : 0, 33 | "oAP" : "\/.github\/ISSUE_TEMPLATE\/config.yml", 34 | "oF" : 0 35 | }, 36 | "\/.github\/ISSUE_TEMPLATE\/feature_request.yaml" : { 37 | "cB" : 0, 38 | "ft" : 8192, 39 | "hM" : 0, 40 | "oA" : 0, 41 | "oAP" : "\/.github\/ISSUE_TEMPLATE\/feature_request.yaml", 42 | "oF" : 0 43 | }, 44 | "\/.github\/ISSUE_TEMPLATE\/support_request.yaml" : { 45 | "cB" : 0, 46 | "ft" : 8192, 47 | "hM" : 0, 48 | "oA" : 0, 49 | "oAP" : "\/.github\/ISSUE_TEMPLATE\/support_request.yaml", 50 | "oF" : 0 51 | }, 52 | "\/.gitignore" : { 53 | "cB" : 0, 54 | "ft" : 8192, 55 | "hM" : 0, 56 | "oA" : 0, 57 | "oAP" : "\/.gitignore", 58 | "oF" : 0 59 | }, 60 | "\/CHANGELOG.md" : { 61 | "cB" : 0, 62 | "cS" : 0, 63 | "eF" : 1, 64 | "eL" : 1, 65 | "ema" : 1, 66 | "eSQ" : 1, 67 | "ft" : 4096, 68 | "hM" : 0, 69 | "oA" : 1, 70 | "oAP" : "\/CHANGELOG.html", 71 | "oF" : 0, 72 | "oFM" : 0, 73 | "oS" : 0, 74 | "pHT" : 0, 75 | "pME" : 1, 76 | "rFN" : 0, 77 | "uCM" : 0 78 | }, 79 | "\/composer.json" : { 80 | "ft" : 524288, 81 | "oA" : 1, 82 | "oAP" : "\/composer-min.json", 83 | "oF" : 0, 84 | "oO" : 1, 85 | "oS" : 1 86 | }, 87 | "\/docs\/.sidebar.json" : { 88 | "ft" : 524288, 89 | "oA" : 1, 90 | "oAP" : "\/docs\/.sidebar-min.json", 91 | "oF" : 0, 92 | "oO" : 1, 93 | "oS" : 1 94 | }, 95 | "\/docs\/feature-tour\/usage.md" : { 96 | "cB" : 0, 97 | "cS" : 0, 98 | "eF" : 1, 99 | "eL" : 1, 100 | "ema" : 1, 101 | "eSQ" : 1, 102 | "ft" : 4096, 103 | "hM" : 0, 104 | "oA" : 1, 105 | "oAP" : "\/docs\/feature-tour\/usage.html", 106 | "oF" : 0, 107 | "oFM" : 0, 108 | "oS" : 0, 109 | "pHT" : 0, 110 | "pME" : 1, 111 | "rFN" : 0, 112 | "uCM" : 0 113 | }, 114 | "\/docs\/get-started\/installation-setup.md" : { 115 | "cB" : 0, 116 | "cS" : 0, 117 | "eF" : 1, 118 | "eL" : 1, 119 | "ema" : 1, 120 | "eSQ" : 1, 121 | "ft" : 4096, 122 | "hM" : 0, 123 | "oA" : 1, 124 | "oAP" : "\/docs\/get-started\/installation-setup.html", 125 | "oF" : 0, 126 | "oFM" : 0, 127 | "oS" : 0, 128 | "pHT" : 0, 129 | "pME" : 1, 130 | "rFN" : 0, 131 | "uCM" : 0 132 | }, 133 | "\/docs\/get-started\/requirements.md" : { 134 | "cB" : 0, 135 | "cS" : 0, 136 | "eF" : 1, 137 | "eL" : 1, 138 | "ema" : 1, 139 | "eSQ" : 1, 140 | "ft" : 4096, 141 | "hM" : 0, 142 | "oA" : 1, 143 | "oAP" : "\/docs\/get-started\/requirements.html", 144 | "oF" : 0, 145 | "oFM" : 0, 146 | "oS" : 0, 147 | "pHT" : 0, 148 | "pME" : 1, 149 | "rFN" : 0, 150 | "uCM" : 0 151 | }, 152 | "\/docs\/README.md" : { 153 | "cB" : 0, 154 | "cS" : 0, 155 | "eF" : 1, 156 | "eL" : 1, 157 | "ema" : 1, 158 | "eSQ" : 1, 159 | "ft" : 4096, 160 | "hM" : 0, 161 | "oA" : 1, 162 | "oAP" : "\/docs\/README.html", 163 | "oF" : 0, 164 | "oFM" : 0, 165 | "oS" : 0, 166 | "pHT" : 0, 167 | "pME" : 1, 168 | "rFN" : 0, 169 | "uCM" : 0 170 | }, 171 | "\/LICENSE.md" : { 172 | "cB" : 0, 173 | "cS" : 0, 174 | "eF" : 1, 175 | "eL" : 1, 176 | "ema" : 1, 177 | "eSQ" : 1, 178 | "ft" : 4096, 179 | "hM" : 0, 180 | "oA" : 1, 181 | "oAP" : "\/LICENSE.html", 182 | "oF" : 0, 183 | "oFM" : 0, 184 | "oS" : 0, 185 | "pHT" : 0, 186 | "pME" : 1, 187 | "rFN" : 0, 188 | "uCM" : 0 189 | }, 190 | "\/README.md" : { 191 | "cB" : 0, 192 | "cS" : 0, 193 | "eF" : 1, 194 | "eL" : 1, 195 | "ema" : 1, 196 | "eSQ" : 1, 197 | "ft" : 4096, 198 | "hM" : 0, 199 | "oA" : 1, 200 | "oAP" : "\/README.html", 201 | "oF" : 0, 202 | "oFM" : 0, 203 | "oS" : 0, 204 | "pHT" : 0, 205 | "pME" : 1, 206 | "rFN" : 0, 207 | "uCM" : 0 208 | }, 209 | "\/src\/assetbundles\/field\/dist\/many-to-many.js" : { 210 | "bF" : 0, 211 | "ft" : 64, 212 | "ma" : 1, 213 | "mi" : 1, 214 | "oA" : 0, 215 | "oAP" : "\/src\/assetbundles\/dist\/js\/many-to-many.js", 216 | "oF" : 0, 217 | "sC" : 0, 218 | "tS" : 0 219 | }, 220 | "\/src\/assetbundles\/field\/ManyToManyFieldAsset.php" : { 221 | "cB" : 0, 222 | "ft" : 8192, 223 | "hM" : 0, 224 | "oA" : 0, 225 | "oAP" : "\/src\/assetbundles\/field\/ManyToManyFieldAsset.php", 226 | "oF" : 0 227 | }, 228 | "\/src\/assetbundles\/field\/src\/many-to-many.js" : { 229 | "bF" : 0, 230 | "ft" : 64, 231 | "ma" : 1, 232 | "mi" : 1, 233 | "oA" : 0, 234 | "oAP" : "\/src\/assetbundles\/dist\/js\/many-to-many.js", 235 | "oF" : 0, 236 | "sC" : 0, 237 | "tS" : 0 238 | }, 239 | "\/src\/base\/PluginTrait.php" : { 240 | "cB" : 0, 241 | "ft" : 8192, 242 | "hM" : 0, 243 | "oA" : 0, 244 | "oAP" : "\/src\/base\/PluginTrait.php", 245 | "oF" : 0 246 | }, 247 | "\/src\/fields\/ManyToManyField.php" : { 248 | "cB" : 0, 249 | "ft" : 8192, 250 | "hM" : 0, 251 | "oA" : 0, 252 | "oAP" : "\/src\/fields\/ManyToManyField.php", 253 | "oF" : 0 254 | }, 255 | "\/src\/icon-mask.svg" : { 256 | "ft" : 2097152, 257 | "miP" : 0, 258 | "oA" : 2, 259 | "oAP" : "\/src\/icon-mask.svg", 260 | "oF" : 0, 261 | "opt" : 0, 262 | "plM" : 52780316221407, 263 | "prP" : 0 264 | }, 265 | "\/src\/icon.svg" : { 266 | "ft" : 2097152, 267 | "miP" : 0, 268 | "oA" : 2, 269 | "oAP" : "\/src\/icon.svg", 270 | "oF" : 0, 271 | "opt" : 0, 272 | "plM" : 52780316221407, 273 | "prP" : 0 274 | }, 275 | "\/src\/ManyToMany.php" : { 276 | "cB" : 0, 277 | "ft" : 8192, 278 | "hM" : 0, 279 | "oA" : 0, 280 | "oAP" : "\/src\/ManyToMany.php", 281 | "oF" : 0 282 | }, 283 | "\/src\/services\/Service.php" : { 284 | "cB" : 0, 285 | "ft" : 8192, 286 | "hM" : 0, 287 | "oA" : 0, 288 | "oAP" : "\/src\/services\/Service.php", 289 | "oF" : 0 290 | }, 291 | "\/src\/templates\/field\/input.html" : { 292 | "cB" : 0, 293 | "ft" : 8192, 294 | "hM" : 0, 295 | "oA" : 0, 296 | "oAP" : "\/src\/templates\/field\/input.html", 297 | "oF" : 0 298 | }, 299 | "\/src\/templates\/field\/settings.html" : { 300 | "cB" : 0, 301 | "ft" : 8192, 302 | "hM" : 0, 303 | "oA" : 0, 304 | "oAP" : "\/src\/templates\/field\/settings.html", 305 | "oF" : 0 306 | }, 307 | "\/src\/translations\/en\/manytomany.php" : { 308 | "cB" : 0, 309 | "ft" : 8192, 310 | "hM" : 0, 311 | "oA" : 0, 312 | "oAP" : "\/src\/translations\/en\/manytomany.php", 313 | "oF" : 0 314 | } 315 | }, 316 | "hooks" : [ 317 | 318 | ], 319 | "manualImportLinks" : { 320 | 321 | }, 322 | "projectAttributes" : { 323 | "creationDate" : 718148775.96866202, 324 | "displayValue" : "many-to-many", 325 | "displayValueWasSetByUser" : 0, 326 | "iconImageName" : "brackets-verd", 327 | "iconImageWasSetByUser" : 0 328 | }, 329 | "projectSettings" : { 330 | "abortBuildOnError" : 1, 331 | "allowInjectionReloads" : 1, 332 | "alwaysUseExternalServer" : 0, 333 | "animateCSSInjections" : 0, 334 | "autoBuildNewItems" : 1, 335 | "autoprefixerEnableIEGrid" : 0, 336 | "babel7PresetType" : 1, 337 | "babelAllowRCFiles" : 0, 338 | "babelAuxiliaryCommentAfter" : "", 339 | "babelAuxiliaryCommentBefore" : "", 340 | "babelConfigType" : 0, 341 | "babelCustomPluginsList" : "", 342 | "babelCustomPresetsList" : "", 343 | "babelExcludeString" : "\/\\\/node_modules\\\/\/, \/\\\/core-js\\\/\/, \/\\\/bower_components\\\/\/", 344 | "babelInsertModuleIDs" : 0, 345 | "babelModuleID" : "", 346 | "babelNoComments" : 0, 347 | "babelPlugins" : { 348 | "arrow-functions" : { 349 | "active" : 0 350 | }, 351 | "async-generator-functions" : { 352 | "active" : 0 353 | }, 354 | "async-to-generator" : { 355 | "active" : 0 356 | }, 357 | "block-scoped-functions" : { 358 | "active" : 0 359 | }, 360 | "block-scoping" : { 361 | "active" : 0 362 | }, 363 | "class-properties" : { 364 | "active" : 0 365 | }, 366 | "classes" : { 367 | "active" : 0 368 | }, 369 | "computed-properties" : { 370 | "active" : 0 371 | }, 372 | "decorators" : { 373 | "active" : 0 374 | }, 375 | "destructuring" : { 376 | "active" : 0 377 | }, 378 | "do-expressions" : { 379 | "active" : 0 380 | }, 381 | "dotall-regex" : { 382 | "active" : 0 383 | }, 384 | "duplicate-keys" : { 385 | "active" : 0 386 | }, 387 | "exponentiation-operator" : { 388 | "active" : 0 389 | }, 390 | "export-default-from" : { 391 | "active" : 0 392 | }, 393 | "export-namespace-from" : { 394 | "active" : 0 395 | }, 396 | "external-helpers" : { 397 | "active" : 0 398 | }, 399 | "flow-strip-types" : { 400 | "active" : 0 401 | }, 402 | "for-of" : { 403 | "active" : 0 404 | }, 405 | "function-bind" : { 406 | "active" : 0 407 | }, 408 | "function-name" : { 409 | "active" : 0 410 | }, 411 | "function-sent" : { 412 | "active" : 0 413 | }, 414 | "inline-consecutive-adds" : { 415 | "active" : 0 416 | }, 417 | "inline-environment-variables" : { 418 | "active" : 0 419 | }, 420 | "instanceof" : { 421 | "active" : 0 422 | }, 423 | "jscript" : { 424 | "active" : 0 425 | }, 426 | "literals" : { 427 | "active" : 0 428 | }, 429 | "logical-assignment-operators" : { 430 | "active" : 0 431 | }, 432 | "member-expression-literals" : { 433 | "active" : 0 434 | }, 435 | "merge-sibling-variables" : { 436 | "active" : 0 437 | }, 438 | "minify-booleans" : { 439 | "active" : 0 440 | }, 441 | "minify-builtins" : { 442 | "active" : 0 443 | }, 444 | "minify-constant-folding" : { 445 | "active" : 0 446 | }, 447 | "minify-dead-code-elimination" : { 448 | "active" : 0 449 | }, 450 | "minify-flip-comparisons" : { 451 | "active" : 0 452 | }, 453 | "minify-guarded-expressions" : { 454 | "active" : 0 455 | }, 456 | "minify-infinity" : { 457 | "active" : 0 458 | }, 459 | "minify-mangle-names" : { 460 | "active" : 0 461 | }, 462 | "minify-numeric-literals" : { 463 | "active" : 0 464 | }, 465 | "minify-simplify" : { 466 | "active" : 0 467 | }, 468 | "minify-type-constructors" : { 469 | "active" : 0 470 | }, 471 | "modules-amd" : { 472 | "active" : 0 473 | }, 474 | "modules-commonjs" : { 475 | "active" : 0 476 | }, 477 | "modules-systemjs" : { 478 | "active" : 0 479 | }, 480 | "modules-umd" : { 481 | "active" : 0 482 | }, 483 | "named-capturing-groups-regex" : { 484 | "active" : 0 485 | }, 486 | "new-target" : { 487 | "active" : 0 488 | }, 489 | "node-env-inline" : { 490 | "active" : 0 491 | }, 492 | "nullish-coalescing-operator" : { 493 | "active" : 0 494 | }, 495 | "numeric-separator" : { 496 | "active" : 0 497 | }, 498 | "object-assign" : { 499 | "active" : 0 500 | }, 501 | "object-rest-spread" : { 502 | "active" : 0 503 | }, 504 | "object-set-prototype-of-to-assign" : { 505 | "active" : 0 506 | }, 507 | "object-super" : { 508 | "active" : 0 509 | }, 510 | "optional-catch-binding" : { 511 | "active" : 0 512 | }, 513 | "optional-chaining" : { 514 | "active" : 0 515 | }, 516 | "parameters" : { 517 | "active" : 0 518 | }, 519 | "partial-application" : { 520 | "active" : 0 521 | }, 522 | "pipeline-operator" : { 523 | "active" : 0 524 | }, 525 | "private-methods" : { 526 | "active" : 0 527 | }, 528 | "property-literals" : { 529 | "active" : 0 530 | }, 531 | "property-mutators" : { 532 | "active" : 0 533 | }, 534 | "proto-to-assign" : { 535 | "active" : 0 536 | }, 537 | "react-constant-elements" : { 538 | "active" : 0 539 | }, 540 | "react-display-name" : { 541 | "active" : 0 542 | }, 543 | "react-inline-elements" : { 544 | "active" : 0 545 | }, 546 | "react-jsx" : { 547 | "active" : 0 548 | }, 549 | "react-jsx-compat" : { 550 | "active" : 0 551 | }, 552 | "react-jsx-self" : { 553 | "active" : 0 554 | }, 555 | "react-jsx-source" : { 556 | "active" : 0 557 | }, 558 | "regenerator" : { 559 | "active" : 0 560 | }, 561 | "regexp-constructors" : { 562 | "active" : 0 563 | }, 564 | "remove-console" : { 565 | "active" : 0 566 | }, 567 | "remove-debugger" : { 568 | "active" : 0 569 | }, 570 | "remove-undefined" : { 571 | "active" : 0 572 | }, 573 | "reserved-words" : { 574 | "active" : 0 575 | }, 576 | "runtime" : { 577 | "active" : 0 578 | }, 579 | "shorthand-properties" : { 580 | "active" : 0 581 | }, 582 | "simplify-comparison-operators" : { 583 | "active" : 0 584 | }, 585 | "spread" : { 586 | "active" : 0 587 | }, 588 | "sticky-regex" : { 589 | "active" : 0 590 | }, 591 | "strict-mode" : { 592 | "active" : 0 593 | }, 594 | "template-literals" : { 595 | "active" : 0 596 | }, 597 | "throw-expressions" : { 598 | "active" : 0 599 | }, 600 | "typeof-symbol" : { 601 | "active" : 0 602 | }, 603 | "undefined-to-void" : { 604 | "active" : 0 605 | }, 606 | "unicode-property-regex" : { 607 | "active" : 0 608 | }, 609 | "unicode-regex" : { 610 | "active" : 0 611 | } 612 | }, 613 | "babelRetainLines" : 0, 614 | "babelUseBuiltInsType" : 0, 615 | "bowerAbbreviatedPath" : "bower_components", 616 | "bowerForceLatestOnConflict" : 1, 617 | "bowerTargetDependencyListType" : 1, 618 | "bowerUseExactVersion" : 0, 619 | "browserRefreshDelay" : 0, 620 | "browserslistString" : ">0.2%, last 2 versions, Firefox ESR, not dead", 621 | "buildEnvironment" : 0, 622 | "buildFolderActive" : 0, 623 | "buildFolderName" : "build", 624 | "cleanBuild" : 1, 625 | "cssoForceMediaMerge" : 0, 626 | "cssoRestructure" : 1, 627 | "environmentVariableEntries" : [ 628 | "NODE_ENV:::production" 629 | ], 630 | "esLintConfigFileHandlingType" : 0, 631 | "esLintECMAVersion" : 7, 632 | "esLintEnvironmentsMask" : 1, 633 | "esLintRules" : { 634 | "accessor-pairs" : { 635 | "active" : 0, 636 | "optionString" : "{'setWithoutGet': true, 'getWithoutSet': false, 'enforceForClassMembers': true}" 637 | }, 638 | "array-bracket-newline" : { 639 | "active" : 0, 640 | "optionString" : "{'multiline': true, 'minItems': null}" 641 | }, 642 | "array-bracket-spacing" : { 643 | "active" : 0, 644 | "optionString" : "'never', {'singleValue': false, 'objectsInArrays': false, 'arraysInArrays': false}" 645 | }, 646 | "array-callback-return" : { 647 | "active" : 0, 648 | "optionString" : "{'allowImplicit': false}" 649 | }, 650 | "array-element-newline" : { 651 | "active" : 0, 652 | "optionString" : "'always'" 653 | }, 654 | "arrow-body-style" : { 655 | "active" : 0, 656 | "optionString" : "'as-needed', {'requireReturnForObjectLiteral': false}" 657 | }, 658 | "arrow-parens" : { 659 | "active" : 0, 660 | "optionString" : "'always'" 661 | }, 662 | "arrow-spacing" : { 663 | "active" : 0, 664 | "optionString" : "{'before': true, 'after': true}" 665 | }, 666 | "block-scoped-var" : { 667 | "active" : 0 668 | }, 669 | "block-spacing" : { 670 | "active" : 0, 671 | "optionString" : "'always'" 672 | }, 673 | "brace-style" : { 674 | "active" : 0, 675 | "optionString" : "'1tbs', {'allowSingleLine': true}" 676 | }, 677 | "camelcase" : { 678 | "active" : 0, 679 | "optionString" : "{'properties': 'always', 'ignoreDestructuring': false, 'ignoreImports': false}" 680 | }, 681 | "capitalized-comments" : { 682 | "active" : 0, 683 | "optionString" : "'always', {'ignoreInlineComments': false, 'ignoreConsecutiveComments': false}" 684 | }, 685 | "class-methods-use-this" : { 686 | "active" : 0, 687 | "optionString" : "{'exceptMethods': [], 'enforceForClassFields': true}" 688 | }, 689 | "comma-dangle" : { 690 | "active" : 1, 691 | "optionString" : "'never'" 692 | }, 693 | "comma-spacing" : { 694 | "active" : 0, 695 | "optionString" : "{'before': false, 'after': true}" 696 | }, 697 | "comma-style" : { 698 | "active" : 0, 699 | "optionString" : "'last'" 700 | }, 701 | "complexity" : { 702 | "active" : 0, 703 | "optionString" : "20" 704 | }, 705 | "computed-property-spacing" : { 706 | "active" : 0, 707 | "optionString" : "'never', {'enforceForClassMembers': true}" 708 | }, 709 | "consistent-return" : { 710 | "active" : 0, 711 | "optionString" : "{'treatUndefinedAsUnspecified': false}" 712 | }, 713 | "consistent-this" : { 714 | "active" : 0, 715 | "optionString" : "'that'" 716 | }, 717 | "constructor-super" : { 718 | "active" : 1 719 | }, 720 | "curly" : { 721 | "active" : 0, 722 | "optionString" : "'all'" 723 | }, 724 | "default-case" : { 725 | "active" : 0 726 | }, 727 | "default-case-last" : { 728 | "active" : 0 729 | }, 730 | "default-param-last" : { 731 | "active" : 0 732 | }, 733 | "dot-location" : { 734 | "active" : 0, 735 | "optionString" : "'object'" 736 | }, 737 | "dot-notation" : { 738 | "active" : 0, 739 | "optionString" : "{'allowKeywords': false}" 740 | }, 741 | "eol-last" : { 742 | "active" : 0, 743 | "optionString" : "'always'" 744 | }, 745 | "eqeqeq" : { 746 | "active" : 0, 747 | "optionString" : "'always', {'null': 'always'}" 748 | }, 749 | "for-direction" : { 750 | "active" : 1 751 | }, 752 | "func-call-spacing" : { 753 | "active" : 0, 754 | "optionString" : "'never'" 755 | }, 756 | "func-name-matching" : { 757 | "active" : 0, 758 | "optionString" : "'always', {'considerPropertyDescriptor': false, 'includeCommonJSModuleExports': false}" 759 | }, 760 | "func-names" : { 761 | "active" : 0, 762 | "optionString" : "'always', {'generators': 'always'}" 763 | }, 764 | "func-style" : { 765 | "active" : 0, 766 | "optionString" : "'expression'" 767 | }, 768 | "function-call-argument-newline" : { 769 | "active" : 0, 770 | "optionString" : "'always'" 771 | }, 772 | "function-paren-newline" : { 773 | "active" : 0, 774 | "optionString" : "'multiline'" 775 | }, 776 | "generator-star-spacing" : { 777 | "active" : 0, 778 | "optionString" : "{'before': true, 'after': false}" 779 | }, 780 | "getter-return" : { 781 | "active" : 1, 782 | "optionString" : "{'allowImplicit': false}" 783 | }, 784 | "grouped-accessor-pairs" : { 785 | "active" : 0, 786 | "optionString" : "'anyOrder'" 787 | }, 788 | "guard-for-in" : { 789 | "active" : 0 790 | }, 791 | "id-denylist" : { 792 | "active" : 0, 793 | "optionString" : "'data', 'err', 'e', 'cb', 'callback'" 794 | }, 795 | "id-length" : { 796 | "active" : 0, 797 | "optionString" : "{'min': 2, 'max': 1000, 'properties': 'always', 'exceptions': ['x', 'i', 'y']}" 798 | }, 799 | "id-match" : { 800 | "active" : 0, 801 | "optionString" : "'^[a-z]+([A-Z][a-z]+)*$', {'properties': false, 'onlyDeclarations': true, 'ignoreDestructuring': false}" 802 | }, 803 | "implicit-arrow-linebreak" : { 804 | "active" : 0, 805 | "optionString" : "'beside'" 806 | }, 807 | "indent" : { 808 | "active" : 0, 809 | "optionString" : "4, {'SwitchCase': 0, 'VariableDeclarator': 1, 'outerIIFEBody': 1 }" 810 | }, 811 | "init-declarations" : { 812 | "active" : 0, 813 | "optionString" : "'always', {'ignoreForLoopInit': true}" 814 | }, 815 | "jsx-quotes" : { 816 | "active" : 0, 817 | "optionString" : "'prefer-double'" 818 | }, 819 | "key-spacing" : { 820 | "active" : 0, 821 | "optionString" : "{'singleLine': {'beforeColon': false, 'afterColon': true, 'mode':'strict'}, 'multiLine': {'beforeColon': false, 'afterColon': true, 'align': 'value', 'mode':'minimum'}}" 822 | }, 823 | "keyword-spacing" : { 824 | "active" : 0, 825 | "optionString" : "{'before': true, 'after': true, 'overrides': {}}" 826 | }, 827 | "line-comment-position" : { 828 | "active" : 0, 829 | "optionString" : "{'position': 'above'}" 830 | }, 831 | "linebreak-style" : { 832 | "active" : 0, 833 | "optionString" : "'unix'" 834 | }, 835 | "lines-around-comment" : { 836 | "active" : 0, 837 | "optionString" : "{'beforeBlockComment': true}" 838 | }, 839 | "lines-between-class-members" : { 840 | "active" : 0, 841 | "optionString" : "'always', {exceptAfterSingleLine: false}" 842 | }, 843 | "logical-assignment-operators" : { 844 | "active" : 0, 845 | "optionString" : "'always', {'enforceForIfStatements': false}" 846 | }, 847 | "max-classes-per-file" : { 848 | "active" : 0, 849 | "optionString" : "{'ignoreExpressions': false, 'max': 1}" 850 | }, 851 | "max-depth" : { 852 | "active" : 0, 853 | "optionString" : "{'max': 4}" 854 | }, 855 | "max-len" : { 856 | "active" : 0, 857 | "optionString" : "{'code': 80, 'comments': 80, 'tabWidth': 4, 'ignoreUrls': true, 'ignoreStrings': true, 'ignoreTemplateLiterals': true, 'ignoreRegExpLiterals': true}" 858 | }, 859 | "max-lines" : { 860 | "active" : 0, 861 | "optionString" : "{'max': 300, 'skipBlankLines': true, 'skipComments': true}" 862 | }, 863 | "max-lines-per-function" : { 864 | "active" : 0, 865 | "optionString" : "{'max': 50, 'skipBlankLines': true, 'skipComments': true, 'IIFEs': false}" 866 | }, 867 | "max-nested-callbacks" : { 868 | "active" : 0, 869 | "optionString" : "{'max': 10}" 870 | }, 871 | "max-params" : { 872 | "active" : 0, 873 | "optionString" : "{'max': 4}" 874 | }, 875 | "max-statements" : { 876 | "active" : 0, 877 | "optionString" : "{'max': 10}, {'ignoreTopLevelFunctions': true}" 878 | }, 879 | "max-statements-per-line" : { 880 | "active" : 0, 881 | "optionString" : "{'max': 1}" 882 | }, 883 | "multiline-comment-style" : { 884 | "active" : 0, 885 | "optionString" : "'starred-block'" 886 | }, 887 | "multiline-ternary" : { 888 | "active" : 0, 889 | "optionString" : "'always'" 890 | }, 891 | "new-cap" : { 892 | "active" : 0, 893 | "optionString" : "{'newIsCap': true, 'capIsNew': true, 'newIsCapExceptions': [], 'capIsNewExceptions': ['Array', 'Boolean', 'Date', 'Error', 'Function', 'Number', 'Object', 'RegExp', 'String', 'Symbol'], 'properties': true}" 894 | }, 895 | "new-parens" : { 896 | "active" : 0, 897 | "optionString" : "'always'" 898 | }, 899 | "newline-per-chained-call" : { 900 | "active" : 0, 901 | "optionString" : "{'ignoreChainWithDepth': 2}" 902 | }, 903 | "no-alert" : { 904 | "active" : 0 905 | }, 906 | "no-array-constructor" : { 907 | "active" : 0 908 | }, 909 | "no-async-promise-executor" : { 910 | "active" : 1 911 | }, 912 | "no-await-in-loop" : { 913 | "active" : 0 914 | }, 915 | "no-bitwise" : { 916 | "active" : 0, 917 | "optionString" : "{'allow': ['~'], 'int32Hint': true}" 918 | }, 919 | "no-caller" : { 920 | "active" : 0 921 | }, 922 | "no-case-declarations" : { 923 | "active" : 1 924 | }, 925 | "no-class-assign" : { 926 | "active" : 1 927 | }, 928 | "no-compare-neg-zero" : { 929 | "active" : 0 930 | }, 931 | "no-cond-assign" : { 932 | "active" : 1, 933 | "optionString" : "'except-parens'" 934 | }, 935 | "no-confusing-arrow" : { 936 | "active" : 0, 937 | "optionString" : "{'allowParens': true, 'onlyOneSimpleParam': false}" 938 | }, 939 | "no-console" : { 940 | "active" : 1, 941 | "optionString" : "{'allow': ['warn', 'error']}" 942 | }, 943 | "no-const-assign" : { 944 | "active" : 1 945 | }, 946 | "no-constant-binary-expression" : { 947 | "active" : 0 948 | }, 949 | "no-constant-condition" : { 950 | "active" : 1, 951 | "optionString" : "{'checkLoops': true}" 952 | }, 953 | "no-constructor-return" : { 954 | "active" : 0 955 | }, 956 | "no-continue" : { 957 | "active" : 0 958 | }, 959 | "no-control-regex" : { 960 | "active" : 1 961 | }, 962 | "no-debugger" : { 963 | "active" : 1 964 | }, 965 | "no-delete-var" : { 966 | "active" : 1 967 | }, 968 | "no-div-regex" : { 969 | "active" : 0 970 | }, 971 | "no-dupe-args" : { 972 | "active" : 1 973 | }, 974 | "no-dupe-class-members" : { 975 | "active" : 1 976 | }, 977 | "no-dupe-else-if" : { 978 | "active" : 1 979 | }, 980 | "no-dupe-keys" : { 981 | "active" : 1 982 | }, 983 | "no-duplicate-case" : { 984 | "active" : 1 985 | }, 986 | "no-duplicate-imports" : { 987 | "active" : 0, 988 | "optionString" : "{'includeExports': false}" 989 | }, 990 | "no-else-return" : { 991 | "active" : 0 992 | }, 993 | "no-empty" : { 994 | "active" : 1, 995 | "optionString" : "{'allowEmptyCatch': false}" 996 | }, 997 | "no-empty-character-class" : { 998 | "active" : 1 999 | }, 1000 | "no-empty-function" : { 1001 | "active" : 0, 1002 | "optionString" : "{'allow': []}" 1003 | }, 1004 | "no-empty-pattern" : { 1005 | "active" : 1 1006 | }, 1007 | "no-empty-static-block" : { 1008 | "active" : 0 1009 | }, 1010 | "no-eq-null" : { 1011 | "active" : 0 1012 | }, 1013 | "no-eval" : { 1014 | "active" : 0, 1015 | "optionString" : "{'allowIndirect': false}" 1016 | }, 1017 | "no-ex-assign" : { 1018 | "active" : 1 1019 | }, 1020 | "no-extend-native" : { 1021 | "active" : 0, 1022 | "optionString" : "{'exceptions': []}" 1023 | }, 1024 | "no-extra-bind" : { 1025 | "active" : 0 1026 | }, 1027 | "no-extra-boolean-cast" : { 1028 | "active" : 1 1029 | }, 1030 | "no-extra-label" : { 1031 | "active" : 0 1032 | }, 1033 | "no-extra-parens" : { 1034 | "active" : 0, 1035 | "optionString" : "'all'" 1036 | }, 1037 | "no-extra-semi" : { 1038 | "active" : 1 1039 | }, 1040 | "no-fallthrough" : { 1041 | "active" : 1, 1042 | "optionString" : "{'allowEmptyCase': false}" 1043 | }, 1044 | "no-floating-decimal" : { 1045 | "active" : 0 1046 | }, 1047 | "no-func-assign" : { 1048 | "active" : 1 1049 | }, 1050 | "no-global-assign" : { 1051 | "active" : 1, 1052 | "optionString" : "{'exceptions': []}" 1053 | }, 1054 | "no-implicit-coercion" : { 1055 | "active" : 0, 1056 | "optionString" : "{'boolean': true, 'number': true, 'string': true, 'disallowTemplateShorthand': false, 'allow': []}" 1057 | }, 1058 | "no-implicit-globals" : { 1059 | "active" : 0 1060 | }, 1061 | "no-implied-eval" : { 1062 | "active" : 0 1063 | }, 1064 | "no-import-assign" : { 1065 | "active" : 1 1066 | }, 1067 | "no-inline-comments" : { 1068 | "active" : 0 1069 | }, 1070 | "no-inner-declarations" : { 1071 | "active" : 1, 1072 | "optionString" : "'functions'" 1073 | }, 1074 | "no-invalid-regexp" : { 1075 | "active" : 1, 1076 | "optionString" : "{'allowConstructorFlags': ['u', 'y']}" 1077 | }, 1078 | "no-invalid-this" : { 1079 | "active" : 0, 1080 | "optionString" : "{'capIsConstructor': true}" 1081 | }, 1082 | "no-irregular-whitespace" : { 1083 | "active" : 1, 1084 | "optionString" : "{'skipStrings': true, 'skipComments': false, 'skipRegExps': true, 'skipTemplates': true}" 1085 | }, 1086 | "no-iterator" : { 1087 | "active" : 0 1088 | }, 1089 | "no-label-var" : { 1090 | "active" : 0 1091 | }, 1092 | "no-labels" : { 1093 | "active" : 0, 1094 | "optionString" : "{'allowLoop': false, 'allowSwitch': false}" 1095 | }, 1096 | "no-lone-blocks" : { 1097 | "active" : 0 1098 | }, 1099 | "no-lonely-if" : { 1100 | "active" : 0 1101 | }, 1102 | "no-loop-func" : { 1103 | "active" : 0 1104 | }, 1105 | "no-loss-of-precision" : { 1106 | "active" : 1 1107 | }, 1108 | "no-magic-numbers" : { 1109 | "active" : 0, 1110 | "optionString" : "{'ignore': [], 'ignoreArrayIndexes': true, 'ignoreDefaultValues': false, 'enforceConst': false, 'detectObjects': false}" 1111 | }, 1112 | "no-misleading-character-class" : { 1113 | "active" : 0 1114 | }, 1115 | "no-mixed-operators" : { 1116 | "active" : 0, 1117 | "optionString" : "{'groups': [['+', '-', '*', '\/', '%', '**'], ['&', '|', '^', '~', '<<', '>>', '>>>'], ['==', '!=', '===', '!==', '>', '>=', '<', '<='], ['&&', '||'], ['in', 'instanceof']], 'allowSamePrecedence': true}" 1118 | }, 1119 | "no-mixed-spaces-and-tabs" : { 1120 | "active" : 1, 1121 | "optionString" : "" 1122 | }, 1123 | "no-multi-assign" : { 1124 | "active" : 0, 1125 | "optionString" : "{'ignoreNonDeclaration': false}" 1126 | }, 1127 | "no-multi-spaces" : { 1128 | "active" : 0, 1129 | "optionString" : "{'exceptions': {'Property': true, 'BinaryExpression': false, 'VariableDeclarator': false, 'ImportDeclaration': false}}" 1130 | }, 1131 | "no-multi-str" : { 1132 | "active" : 0 1133 | }, 1134 | "no-multiple-empty-lines" : { 1135 | "active" : 0, 1136 | "optionString" : "{'max': 2, 'maxBOF': 2, 'maxEOF': 2}" 1137 | }, 1138 | "no-negated-condition" : { 1139 | "active" : 0 1140 | }, 1141 | "no-nested-ternary" : { 1142 | "active" : 0 1143 | }, 1144 | "no-new" : { 1145 | "active" : 0 1146 | }, 1147 | "no-new-func" : { 1148 | "active" : 0 1149 | }, 1150 | "no-new-native-nonconstructor" : { 1151 | "active" : 0 1152 | }, 1153 | "no-new-object" : { 1154 | "active" : 0 1155 | }, 1156 | "no-new-symbol" : { 1157 | "active" : 1 1158 | }, 1159 | "no-new-wrappers" : { 1160 | "active" : 0 1161 | }, 1162 | "no-nonoctal-decimal-escape" : { 1163 | "active" : 1 1164 | }, 1165 | "no-obj-calls" : { 1166 | "active" : 1 1167 | }, 1168 | "no-octal" : { 1169 | "active" : 1 1170 | }, 1171 | "no-octal-escape" : { 1172 | "active" : 0 1173 | }, 1174 | "no-param-reassign" : { 1175 | "active" : 0, 1176 | "optionString" : "{'props': false}" 1177 | }, 1178 | "no-plusplus" : { 1179 | "active" : 0, 1180 | "optionString" : "{'allowForLoopAfterthoughts': false}" 1181 | }, 1182 | "no-promise-executor-return" : { 1183 | "active" : 0 1184 | }, 1185 | "no-proto" : { 1186 | "active" : 0 1187 | }, 1188 | "no-prototype-builtins" : { 1189 | "active" : 1 1190 | }, 1191 | "no-redeclare" : { 1192 | "active" : 1, 1193 | "optionString" : "{'builtinGlobals': false}" 1194 | }, 1195 | "no-regex-spaces" : { 1196 | "active" : 1 1197 | }, 1198 | "no-restricted-exports" : { 1199 | "active" : 0, 1200 | "optionString" : "{'restrictedNamedExports': []}" 1201 | }, 1202 | "no-restricted-globals" : { 1203 | "active" : 0, 1204 | "optionString" : "'event', 'fdescribe'" 1205 | }, 1206 | "no-restricted-imports" : { 1207 | "active" : 0, 1208 | "optionString" : "" 1209 | }, 1210 | "no-restricted-properties" : { 1211 | "active" : 0, 1212 | "optionString" : "[{'object': 'disallowedObjectName', 'property': 'disallowedPropertyName'}, {'object': 'disallowedObjectName', 'property': 'anotherDisallowedPropertyName', 'message': 'Please use allowedObjectName.allowedPropertyName.'}]" 1213 | }, 1214 | "no-restricted-syntax" : { 1215 | "active" : 0, 1216 | "optionString" : "'FunctionExpression', 'WithStatement'" 1217 | }, 1218 | "no-return-assign" : { 1219 | "active" : 0, 1220 | "optionString" : "'except-parens'" 1221 | }, 1222 | "no-script-url" : { 1223 | "active" : 0 1224 | }, 1225 | "no-self-assign" : { 1226 | "active" : 1, 1227 | "optionString" : "{'props': true}" 1228 | }, 1229 | "no-self-compare" : { 1230 | "active" : 0 1231 | }, 1232 | "no-sequences" : { 1233 | "active" : 0, 1234 | "optionString" : "{'allowInParentheses': true}" 1235 | }, 1236 | "no-setter-return" : { 1237 | "active" : 1 1238 | }, 1239 | "no-shadow" : { 1240 | "active" : 0, 1241 | "optionString" : "{'builtinGlobals': false, 'hoist': 'functions', 'allow': [], 'ignoreOnInitialization': false}" 1242 | }, 1243 | "no-shadow-restricted-names" : { 1244 | "active" : 1 1245 | }, 1246 | "no-sparse-arrays" : { 1247 | "active" : 1 1248 | }, 1249 | "no-tabs" : { 1250 | "active" : 0, 1251 | "optionString" : "{allowIndentationTabs: false}" 1252 | }, 1253 | "no-template-curly-in-string" : { 1254 | "active" : 0 1255 | }, 1256 | "no-ternary" : { 1257 | "active" : 0 1258 | }, 1259 | "no-this-before-super" : { 1260 | "active" : 1 1261 | }, 1262 | "no-throw-literal" : { 1263 | "active" : 0 1264 | }, 1265 | "no-trailing-spaces" : { 1266 | "active" : 0, 1267 | "optionString" : "{'skipBlankLines': false, 'ignoreComments': false}" 1268 | }, 1269 | "no-undef" : { 1270 | "active" : 1, 1271 | "optionString" : "{'typeof': false}" 1272 | }, 1273 | "no-undef-init" : { 1274 | "active" : 0 1275 | }, 1276 | "no-undefined" : { 1277 | "active" : 0 1278 | }, 1279 | "no-underscore-dangle" : { 1280 | "active" : 0, 1281 | "optionString" : "{'allow': [], 'allowAfterThis': false, 'allowAfterSuper': false, 'allowAfterThisConstructor': false, 'enforceInMethodNames': false, 'allowFunctionParams': true}" 1282 | }, 1283 | "no-unexpected-multiline" : { 1284 | "active" : 1 1285 | }, 1286 | "no-unmodified-loop-condition" : { 1287 | "active" : 0 1288 | }, 1289 | "no-unneeded-ternary" : { 1290 | "active" : 0, 1291 | "optionString" : "{'defaultAssignment': true}" 1292 | }, 1293 | "no-unreachable" : { 1294 | "active" : 1 1295 | }, 1296 | "no-unreachable-loop" : { 1297 | "active" : 0, 1298 | "optionString" : "{'ignore': []}" 1299 | }, 1300 | "no-unsafe-finally" : { 1301 | "active" : 1 1302 | }, 1303 | "no-unsafe-negation" : { 1304 | "active" : 1, 1305 | "optionString" : "{'enforceForOrderingRelations': false}" 1306 | }, 1307 | "no-unsafe-optional-chaining" : { 1308 | "active" : 1, 1309 | "optionString" : "{'disallowArithmeticOperators': false}" 1310 | }, 1311 | "no-unused-expressions" : { 1312 | "active" : 0, 1313 | "optionString" : "{'allowShortCircuit': false, 'allowTernary': false, 'allowTaggedTemplates': false, 'enforceForJSX': false}" 1314 | }, 1315 | "no-unused-labels" : { 1316 | "active" : 1 1317 | }, 1318 | "no-unused-private-class-members" : { 1319 | "active" : 0 1320 | }, 1321 | "no-unused-vars" : { 1322 | "active" : 1, 1323 | "optionString" : "{'vars': 'all', 'args': 'after-used', 'caughtErrors': 'none', 'ignoreRestSiblings': false}" 1324 | }, 1325 | "no-use-before-define" : { 1326 | "active" : 0, 1327 | "optionString" : "{'functions': true, 'classes': true, 'variables': true}" 1328 | }, 1329 | "no-useless-backreference" : { 1330 | "active" : 1 1331 | }, 1332 | "no-useless-call" : { 1333 | "active" : 0 1334 | }, 1335 | "no-useless-catch" : { 1336 | "active" : 1 1337 | }, 1338 | "no-useless-computed-key" : { 1339 | "active" : 0, 1340 | "optionString" : "{'enforceForClassMembers': false}" 1341 | }, 1342 | "no-useless-concat" : { 1343 | "active" : 0 1344 | }, 1345 | "no-useless-constructor" : { 1346 | "active" : 0 1347 | }, 1348 | "no-useless-escape" : { 1349 | "active" : 1 1350 | }, 1351 | "no-useless-rename" : { 1352 | "active" : 0, 1353 | "optionString" : "{'ignoreDestructuring': false, 'ignoreImport': false, 'ignoreExport': false}" 1354 | }, 1355 | "no-useless-return" : { 1356 | "active" : 0 1357 | }, 1358 | "no-var" : { 1359 | "active" : 0 1360 | }, 1361 | "no-void" : { 1362 | "active" : 0, 1363 | "optionString" : "{'allowAsStatement': false}" 1364 | }, 1365 | "no-warning-comments" : { 1366 | "active" : 0, 1367 | "optionString" : "{'terms': ['todo', 'fixme', 'xxx'], 'location': 'start'}" 1368 | }, 1369 | "no-whitespace-before-property" : { 1370 | "active" : 0 1371 | }, 1372 | "no-with" : { 1373 | "active" : 1 1374 | }, 1375 | "nonblock-statement-body-position" : { 1376 | "active" : 0, 1377 | "optionString" : "'beside'" 1378 | }, 1379 | "object-curly-newline" : { 1380 | "active" : 0, 1381 | "optionString" : "{'ObjectExpression': {'multiline': true, 'consistent': true}, 'ObjectPattern': {'multiline': true, 'consistent': true}}" 1382 | }, 1383 | "object-curly-spacing" : { 1384 | "active" : 0, 1385 | "optionString" : "'never'" 1386 | }, 1387 | "object-property-newline" : { 1388 | "active" : 0, 1389 | "optionString" : "{'allowAllPropertiesOnSameLine': true}" 1390 | }, 1391 | "object-shorthand" : { 1392 | "active" : 0, 1393 | "optionString" : "'always', {'avoidQuotes': false, 'ignoreConstructors': false}" 1394 | }, 1395 | "one-var" : { 1396 | "active" : 0, 1397 | "optionString" : "'always'" 1398 | }, 1399 | "one-var-declaration-per-line" : { 1400 | "active" : 0, 1401 | "optionString" : "'always'" 1402 | }, 1403 | "operator-assignment" : { 1404 | "active" : 0, 1405 | "optionString" : "'always'" 1406 | }, 1407 | "operator-linebreak" : { 1408 | "active" : 0, 1409 | "optionString" : "'after', {'overrides': {'?': 'after', '+=': 'none'}}" 1410 | }, 1411 | "padded-blocks" : { 1412 | "active" : 0, 1413 | "optionString" : "{'blocks': 'always', 'switches': 'always', 'classes': 'always'}" 1414 | }, 1415 | "padding-line-between-statements" : { 1416 | "active" : 0, 1417 | "optionString" : "{blankLine: 'always', prev:'*', next:'return'}" 1418 | }, 1419 | "prefer-arrow-callback" : { 1420 | "active" : 0 1421 | }, 1422 | "prefer-const" : { 1423 | "active" : 0, 1424 | "optionString" : "{'destructuring': 'any', 'ignoreReadBeforeAssign': false}" 1425 | }, 1426 | "prefer-destructuring" : { 1427 | "active" : 0, 1428 | "optionString" : "{'array': true, 'object': true}, {'enforceForRenamedProperties': false}" 1429 | }, 1430 | "prefer-exponentiation-operator" : { 1431 | "active" : 0 1432 | }, 1433 | "prefer-named-capture-group" : { 1434 | "active" : 0 1435 | }, 1436 | "prefer-numeric-literals" : { 1437 | "active" : 0 1438 | }, 1439 | "prefer-object-has-own" : { 1440 | "active" : 0 1441 | }, 1442 | "prefer-object-spread" : { 1443 | "active" : 0 1444 | }, 1445 | "prefer-promise-reject-errors" : { 1446 | "active" : 0, 1447 | "optionString" : "{'allowEmptyReject': false}" 1448 | }, 1449 | "prefer-regex-literals" : { 1450 | "active" : 0 1451 | }, 1452 | "prefer-rest-params" : { 1453 | "active" : 0 1454 | }, 1455 | "prefer-spread" : { 1456 | "active" : 0 1457 | }, 1458 | "prefer-template" : { 1459 | "active" : 0 1460 | }, 1461 | "quote-props" : { 1462 | "active" : 0, 1463 | "optionString" : "'always'" 1464 | }, 1465 | "quotes" : { 1466 | "active" : 0, 1467 | "optionString" : "'double', {'avoidEscape': true, 'allowTemplateLiterals': true}" 1468 | }, 1469 | "radix" : { 1470 | "active" : 0, 1471 | "optionString" : "'always'" 1472 | }, 1473 | "require-atomic-updates" : { 1474 | "active" : 0, 1475 | "optionString" : "{'allowProperties': false}" 1476 | }, 1477 | "require-await" : { 1478 | "active" : 0 1479 | }, 1480 | "require-unicode-regexp" : { 1481 | "active" : 0 1482 | }, 1483 | "require-yield" : { 1484 | "active" : 1 1485 | }, 1486 | "rest-spread-spacing" : { 1487 | "active" : 0, 1488 | "optionString" : "'never'" 1489 | }, 1490 | "semi" : { 1491 | "active" : 0, 1492 | "optionString" : "'always', {'omitLastInOneLineBlock': false}" 1493 | }, 1494 | "semi-spacing" : { 1495 | "active" : 0, 1496 | "optionString" : "{'before': false, 'after': true}" 1497 | }, 1498 | "semi-style" : { 1499 | "active" : 0, 1500 | "optionString" : "'last'" 1501 | }, 1502 | "sort-imports" : { 1503 | "active" : 0, 1504 | "optionString" : "{'ignoreCase': false, 'ignoreMemberSort': true, 'memberSyntaxSortOrder': ['none', 'all', 'multiple', 'single'], 'allowSeparatedGroups': false}" 1505 | }, 1506 | "sort-keys" : { 1507 | "active" : 0, 1508 | "optionString" : "'asc', {'caseSensitive': true, 'natural': false, 'minKeys': 2}" 1509 | }, 1510 | "sort-vars" : { 1511 | "active" : 0, 1512 | "optionString" : "{'ignoreCase': false}" 1513 | }, 1514 | "space-before-blocks" : { 1515 | "active" : 0, 1516 | "optionString" : "{'functions': 'always', 'keywords': 'always', 'classes': 'always'}" 1517 | }, 1518 | "space-before-function-paren" : { 1519 | "active" : 0, 1520 | "optionString" : "{'anonymous': 'always', 'named': 'never'}" 1521 | }, 1522 | "space-in-parens" : { 1523 | "active" : 0, 1524 | "optionString" : "'never', {'exceptions': []}" 1525 | }, 1526 | "space-infix-ops" : { 1527 | "active" : 0, 1528 | "optionString" : "{'int32Hint': false}" 1529 | }, 1530 | "space-unary-ops" : { 1531 | "active" : 0, 1532 | "optionString" : "{'words': true, 'nonwords': false, 'overrides': {}}" 1533 | }, 1534 | "spaced-comment" : { 1535 | "active" : 0, 1536 | "optionString" : "'always', {'line': {'markers': ['\/'], 'exceptions': ['-', '+']}, 'block': {'markers': ['!'], 'exceptions': ['*'], 'balanced': false}}" 1537 | }, 1538 | "strict" : { 1539 | "active" : 0, 1540 | "optionString" : "'safe'" 1541 | }, 1542 | "switch-colon-spacing" : { 1543 | "active" : 0, 1544 | "optionString" : "{'after': true, 'before': false}" 1545 | }, 1546 | "symbol-description" : { 1547 | "active" : 0 1548 | }, 1549 | "template-curly-spacing" : { 1550 | "active" : 0, 1551 | "optionString" : "'never'" 1552 | }, 1553 | "template-tag-spacing" : { 1554 | "active" : 0, 1555 | "optionString" : "'never'" 1556 | }, 1557 | "unicode-bom" : { 1558 | "active" : 0, 1559 | "optionString" : "'never'" 1560 | }, 1561 | "use-isnan" : { 1562 | "active" : 1, 1563 | "optionString" : "{'enforceForSwitchCase': true, 'enforceForIndexOf': false}" 1564 | }, 1565 | "valid-typeof" : { 1566 | "active" : 1, 1567 | "optionString" : "{'requireStringLiterals': true}" 1568 | }, 1569 | "vars-on-top" : { 1570 | "active" : 0 1571 | }, 1572 | "wrap-iife" : { 1573 | "active" : 0, 1574 | "optionString" : "'outside'" 1575 | }, 1576 | "wrap-regex" : { 1577 | "active" : 0 1578 | }, 1579 | "yield-star-spacing" : { 1580 | "active" : 0, 1581 | "optionString" : "{'before': false, 'after': true}" 1582 | }, 1583 | "yoda" : { 1584 | "active" : 0, 1585 | "optionString" : "'never', {'exceptRange': false, 'onlyEquality': false}" 1586 | } 1587 | }, 1588 | "esLintSourceType" : 0, 1589 | "externalServerAddress" : "http:\/\/localhost:8888", 1590 | "gitIgnoreBuildFolder" : 1, 1591 | "hideConfigFile" : 0, 1592 | "jsCheckerReservedNamesString" : "", 1593 | "languageDefaultsCOFFEE" : { 1594 | "autoOutputAction" : 0, 1595 | "autoOutputPathFilenamePattern" : "*.js", 1596 | "autoOutputPathRelativePath" : "", 1597 | "autoOutputPathReplace1" : "", 1598 | "autoOutputPathReplace2" : "", 1599 | "autoOutputPathStyle" : 0, 1600 | "minifierStyle" : 0, 1601 | "outputStyle" : 0, 1602 | "sourceMapStyle" : 0, 1603 | "transpilerStyle" : 1 1604 | }, 1605 | "languageDefaultsCSS" : { 1606 | "autoOutputAction" : 0, 1607 | "autoOutputPathFilenamePattern" : "*-min.css", 1608 | "autoOutputPathRelativePath" : "", 1609 | "autoOutputPathReplace1" : "", 1610 | "autoOutputPathReplace2" : "", 1611 | "autoOutputPathStyle" : 0, 1612 | "combineImports" : 0, 1613 | "cssoStyle" : 0, 1614 | "purgeCSSStyle" : 0, 1615 | "shouldRunAutoprefixer" : 1, 1616 | "shouldRunBless" : 0, 1617 | "sourceMapStyle" : 0 1618 | }, 1619 | "languageDefaultsGIF" : { 1620 | "autoOutputAction" : 0, 1621 | "autoOutputPathFilenamePattern" : "*.gif", 1622 | "autoOutputPathRelativePath" : "", 1623 | "autoOutputPathReplace1" : "", 1624 | "autoOutputPathReplace2" : "", 1625 | "autoOutputPathStyle" : 0, 1626 | "webpOptimizationPresetUUID" : "lpckwebp-none", 1627 | "webpRGBQuality" : 75 1628 | }, 1629 | "languageDefaultsHAML" : { 1630 | "autoOutputAction" : 0, 1631 | "autoOutputPathFilenamePattern" : "*.html", 1632 | "autoOutputPathRelativePath" : "", 1633 | "autoOutputPathReplace1" : "", 1634 | "autoOutputPathReplace2" : "", 1635 | "autoOutputPathStyle" : 0, 1636 | "escapeHTMLCharacters" : 0, 1637 | "htmlMinifierStyle" : 0, 1638 | "noEscapeInAttributes" : 0, 1639 | "outputFormat" : 2, 1640 | "shouldRunCacheBuster" : 0, 1641 | "useCDATA" : 0, 1642 | "useDoubleQuotes" : 0, 1643 | "useUnixNewlines" : 0 1644 | }, 1645 | "languageDefaultsJPG" : { 1646 | "autoOutputAction" : 0, 1647 | "autoOutputPathFilenamePattern" : "*.jpg", 1648 | "autoOutputPathRelativePath" : "", 1649 | "autoOutputPathReplace1" : "", 1650 | "autoOutputPathReplace2" : "", 1651 | "autoOutputPathStyle" : 0, 1652 | "outputFormat" : 0, 1653 | "quality" : 100, 1654 | "webpOptimizationPresetUUID" : "lpckwebp-none", 1655 | "webpRGBQuality" : 75 1656 | }, 1657 | "languageDefaultsJS" : { 1658 | "autoOutputAction" : 0, 1659 | "autoOutputPathFilenamePattern" : "*.js", 1660 | "autoOutputPathRelativePath" : "..\/..\/dist\/js", 1661 | "autoOutputPathReplace1" : "", 1662 | "autoOutputPathReplace2" : "", 1663 | "autoOutputPathStyle" : 2, 1664 | "bundleFormat" : 0, 1665 | "minifierStyle" : 1, 1666 | "sourceMapStyle" : 1, 1667 | "syntaxCheckerStyle" : 0, 1668 | "transpilerStyle" : 0 1669 | }, 1670 | "languageDefaultsJSON" : { 1671 | "autoOutputAction" : 1, 1672 | "autoOutputPathFilenamePattern" : "*-min.json", 1673 | "autoOutputPathRelativePath" : "", 1674 | "autoOutputPathReplace1" : "", 1675 | "autoOutputPathReplace2" : "", 1676 | "autoOutputPathStyle" : 0, 1677 | "orderOutput" : 1, 1678 | "outputStyle" : 1 1679 | }, 1680 | "languageDefaultsKIT" : { 1681 | "autoOutputAction" : 0, 1682 | "autoOutputPathFilenamePattern" : "*.html", 1683 | "autoOutputPathRelativePath" : "", 1684 | "autoOutputPathReplace1" : "kit", 1685 | "autoOutputPathReplace2" : "html", 1686 | "autoOutputPathStyle" : 0, 1687 | "htmlMinifierStyle" : 0, 1688 | "shouldRunCacheBuster" : 0 1689 | }, 1690 | "languageDefaultsLESS" : { 1691 | "allowInsecureImports" : 0, 1692 | "autoOutputAction" : 0, 1693 | "autoOutputPathFilenamePattern" : "*.css", 1694 | "autoOutputPathRelativePath" : "..\/css", 1695 | "autoOutputPathReplace1" : "less", 1696 | "autoOutputPathReplace2" : "css", 1697 | "autoOutputPathStyle" : 0, 1698 | "cssoStyle" : 0, 1699 | "enableJavascript" : 0, 1700 | "mathStyle" : 0, 1701 | "outputStyle" : 0, 1702 | "purgeCSSStyle" : 0, 1703 | "rewriteURLStyle" : 0, 1704 | "shouldRunAutoprefixer" : 0, 1705 | "shouldRunBless" : 0, 1706 | "sourceMapStyle" : 0, 1707 | "strictImports" : 0, 1708 | "strictUnits" : 0 1709 | }, 1710 | "languageDefaultsMARKDOWN" : { 1711 | "autoOutputAction" : 1, 1712 | "autoOutputPathFilenamePattern" : "*.html", 1713 | "autoOutputPathRelativePath" : "", 1714 | "autoOutputPathReplace1" : "", 1715 | "autoOutputPathReplace2" : "", 1716 | "autoOutputPathStyle" : 0, 1717 | "criticStyle" : 0, 1718 | "enableFootnotes" : 1, 1719 | "enableLabels" : 1, 1720 | "enableSmartQuotes" : 1, 1721 | "htmlMinifierStyle" : 0, 1722 | "maskEmailAddresses" : 1, 1723 | "outputFormat" : 0, 1724 | "outputStyle" : 0, 1725 | "parseMetadata" : 1, 1726 | "processHTML" : 0, 1727 | "randomFootnoteNumbers" : 0, 1728 | "shouldRunCacheBuster" : 0, 1729 | "useCompatibilityMode" : 0 1730 | }, 1731 | "languageDefaultsOTHER" : { 1732 | "autoOutputAction" : 0, 1733 | "autoOutputPathFilenamePattern" : "*.*", 1734 | "autoOutputPathRelativePath" : "", 1735 | "autoOutputPathReplace1" : "", 1736 | "autoOutputPathReplace2" : "", 1737 | "autoOutputPathStyle" : 0, 1738 | "htmlMinifierStyle" : 0, 1739 | "shouldRunCacheBuster" : 0 1740 | }, 1741 | "languageDefaultsPNG" : { 1742 | "autoOutputAction" : 0, 1743 | "autoOutputPathFilenamePattern" : "*.png", 1744 | "autoOutputPathRelativePath" : "", 1745 | "autoOutputPathReplace1" : "", 1746 | "autoOutputPathReplace2" : "", 1747 | "autoOutputPathStyle" : 0, 1748 | "optimizerType" : 1, 1749 | "quality" : 100, 1750 | "webpOptimizationPresetUUID" : "lpckwebp-none", 1751 | "webpRGBQuality" : 75 1752 | }, 1753 | "languageDefaultsPUG" : { 1754 | "autoOutputAction" : 0, 1755 | "autoOutputPathFilenamePattern" : "*.html", 1756 | "autoOutputPathRelativePath" : "", 1757 | "autoOutputPathReplace1" : "", 1758 | "autoOutputPathReplace2" : "", 1759 | "autoOutputPathStyle" : 0, 1760 | "compileDebug" : 1, 1761 | "htmlMinifierStyle" : 0, 1762 | "outputStyle" : 1, 1763 | "shouldRunCacheBuster" : 0 1764 | }, 1765 | "languageDefaultsSASS" : { 1766 | "autoOutputAction" : 0, 1767 | "autoOutputPathFilenamePattern" : "*.css", 1768 | "autoOutputPathRelativePath" : "..\/..\/dist\/css", 1769 | "autoOutputPathReplace1" : "sass", 1770 | "autoOutputPathReplace2" : "css", 1771 | "autoOutputPathStyle" : 2, 1772 | "compilerType" : 0, 1773 | "cssoStyle" : 0, 1774 | "decimalPrecision" : 10, 1775 | "emitCharset" : 1, 1776 | "outputStyle" : 3, 1777 | "purgeCSSStyle" : 0, 1778 | "shouldRunAutoprefixer" : 1, 1779 | "shouldRunBless" : 0, 1780 | "sourceMapStyle" : 0 1781 | }, 1782 | "languageDefaultsSLIM" : { 1783 | "autoOutputAction" : 0, 1784 | "autoOutputPathFilenamePattern" : "*.html", 1785 | "autoOutputPathRelativePath" : "", 1786 | "autoOutputPathReplace1" : "", 1787 | "autoOutputPathReplace2" : "", 1788 | "autoOutputPathStyle" : 0, 1789 | "compileOnly" : 0, 1790 | "htmlMinifierStyle" : 0, 1791 | "logicless" : 0, 1792 | "outputFormat" : 0, 1793 | "outputStyle" : 1, 1794 | "railsCompatible" : 0, 1795 | "shouldRunCacheBuster" : 0 1796 | }, 1797 | "languageDefaultsSTYLUS" : { 1798 | "autoOutputAction" : 0, 1799 | "autoOutputPathFilenamePattern" : "*.css", 1800 | "autoOutputPathRelativePath" : "..\/css", 1801 | "autoOutputPathReplace1" : "stylus", 1802 | "autoOutputPathReplace2" : "css", 1803 | "autoOutputPathStyle" : 0, 1804 | "cssoStyle" : 0, 1805 | "debugStyle" : 0, 1806 | "importCSS" : 0, 1807 | "outputStyle" : 0, 1808 | "purgeCSSStyle" : 0, 1809 | "resolveRelativeURLS" : 0, 1810 | "shouldRunAutoprefixer" : 0, 1811 | "shouldRunBless" : 0, 1812 | "sourceMapStyle" : 0 1813 | }, 1814 | "languageDefaultsSVG" : { 1815 | "autoOutputAction" : 2, 1816 | "autoOutputPathFilenamePattern" : "*.svg", 1817 | "autoOutputPathRelativePath" : "", 1818 | "autoOutputPathReplace1" : "", 1819 | "autoOutputPathReplace2" : "", 1820 | "autoOutputPathStyle" : 0, 1821 | "pluginMask" : 52780316221407 1822 | }, 1823 | "languageDefaultsTS" : { 1824 | "autoOutputAction" : 0, 1825 | "autoOutputPathFilenamePattern" : "*.js", 1826 | "autoOutputPathRelativePath" : "\/js", 1827 | "autoOutputPathReplace1" : "", 1828 | "autoOutputPathReplace2" : "", 1829 | "autoOutputPathStyle" : 0, 1830 | "createDeclarationFile" : 0, 1831 | "jsxMode" : 0, 1832 | "minifierStyle" : 0, 1833 | "moduleDetectionType" : 0, 1834 | "moduleResolutionType" : 0, 1835 | "moduleType" : 2, 1836 | "removeComments" : 0, 1837 | "sourceMapStyle" : 0, 1838 | "targetECMAVersion" : 2018 1839 | }, 1840 | "languageDefaultsUserDefined" : [ 1841 | 1842 | ], 1843 | "npmAbbreviatedPath" : "", 1844 | "npmCreatePackageLock" : 1, 1845 | "npmInstallOptionalDependencies" : 0, 1846 | "npmSaveExactVersion" : 0, 1847 | "npmTargetDependencyListType" : 1, 1848 | "overrideExternalServerCSS" : 0, 1849 | "previewPathAddition" : "", 1850 | "purgeCSS" : { 1851 | "blocklistEntries" : [ 1852 | 1853 | ], 1854 | "contentEntries" : [ 1855 | "**\/*.html", 1856 | "**\/*.htm", 1857 | "**\/*.shtml", 1858 | "**\/*.xhtml", 1859 | "**\/*.php", 1860 | "**\/*.js", 1861 | "**\/*.ts", 1862 | "**\/*.coffee", 1863 | "**\/*.erb", 1864 | "**\/*.pug", 1865 | "**\/*.jade", 1866 | "**\/*.slim", 1867 | "**\/*.haml", 1868 | "**\/*.md", 1869 | "**\/*.kit" 1870 | ], 1871 | "removeFontFace" : 0, 1872 | "removeKeyframes" : 0, 1873 | "removeVariables" : 0, 1874 | "safelistEntries" : [ 1875 | 1876 | ], 1877 | "skippedEntries" : [ 1878 | "node_modules\/**" 1879 | ] 1880 | }, 1881 | "rollupContext" : "", 1882 | "rollupExternalEntries" : [ 1883 | 1884 | ], 1885 | "rollupReplacementEntries" : [ 1886 | "process.env.NODE_ENV:::$NODE_ENV", 1887 | "ENVIRONMENT:::$NODE_ENV" 1888 | ], 1889 | "rollupTreeshakingEnabled" : 1, 1890 | "rollupTreeshakingModuleSideEffects" : 1, 1891 | "skippedFoldersString" : "log, _logs, logs, _cache, cache, .idea, \/storage\/framework\/sessions, node_modules", 1892 | "sourceFolderName" : "source", 1893 | "susyVersion" : 3, 1894 | "tsAllowArbitraryExtensions" : 0, 1895 | "tsAllowImportingTSExtensions" : 0, 1896 | "tsAllowSyntheticDefaultImports" : 0, 1897 | "tsAllowUMDGlobalAccess" : 0, 1898 | "tsAllowUnreachableCode" : 0, 1899 | "tsAllowUnusedLabels" : 0, 1900 | "tsAlwaysStrict" : 0, 1901 | "tsDownlevelIteration" : 0, 1902 | "tsEmitBOM" : 0, 1903 | "tsEmitDecoratorMetadata" : 0, 1904 | "tsESModuleInterop" : 0, 1905 | "tsExactOptionalPropertyTypes" : 0, 1906 | "tsExperimentalDecorators" : 0, 1907 | "tsForceConsistentCasingInFileNames" : 0, 1908 | "tsImportHelpers" : 0, 1909 | "tsIsolatedModules" : 0, 1910 | "tsJSXFactory" : "React.createElement", 1911 | "tsNoEmitHelpers" : 0, 1912 | "tsNoFallthroughCasesInSwitch" : 0, 1913 | "tsNoImplicitAny" : 0, 1914 | "tsNoImplicitOverride" : 0, 1915 | "tsNoImplicitReturns" : 0, 1916 | "tsNoImplicitThis" : 0, 1917 | "tsNoLib" : 0, 1918 | "tsNoPropertyAccessFromIndexSignature" : 0, 1919 | "tsNoResolve" : 0, 1920 | "tsNoUncheckedIndexAccess" : 0, 1921 | "tsNoUnusedLocals" : 0, 1922 | "tsNoUnusedParameters" : 0, 1923 | "tsPreserveConstEnums" : 0, 1924 | "tsPreserveSymlinks" : 0, 1925 | "tsResolveJsonModule" : 0, 1926 | "tsSkipDefaultLibCheck" : 0, 1927 | "tsSkipLibCheck" : 0, 1928 | "tsStrictBindCallApply" : 0, 1929 | "tsStrictFunctionTypes" : 0, 1930 | "tsStrictNullChecks" : 0, 1931 | "tsStrictPropertyInitialization" : 0, 1932 | "tsStripInternal" : 0, 1933 | "tsUseDefineForClassFields" : 0, 1934 | "tsUseUnknownInCatchVariables" : 0, 1935 | "tsVerbatimModuleSyntax" : 0, 1936 | "uglifyDefinesString" : "", 1937 | "uglifyFlags2" : { 1938 | "arguments" : { 1939 | "active" : 1, 1940 | "flagValue" : -1 1941 | }, 1942 | "arrows" : { 1943 | "active" : 1, 1944 | "flagValue" : -1 1945 | }, 1946 | "ascii_only" : { 1947 | "active" : 0, 1948 | "flagValue" : -1 1949 | }, 1950 | "booleans" : { 1951 | "active" : 1, 1952 | "flagValue" : -1 1953 | }, 1954 | "booleans_as_integers" : { 1955 | "active" : 0, 1956 | "flagValue" : -1 1957 | }, 1958 | "braces" : { 1959 | "active" : 0, 1960 | "flagValue" : -1 1961 | }, 1962 | "collapse_vars" : { 1963 | "active" : 1, 1964 | "flagValue" : -1 1965 | }, 1966 | "comments" : { 1967 | "active" : 0, 1968 | "flagValue" : 1 1969 | }, 1970 | "comparisons" : { 1971 | "active" : 1, 1972 | "flagValue" : -1 1973 | }, 1974 | "computed_props" : { 1975 | "active" : 1, 1976 | "flagValue" : -1 1977 | }, 1978 | "conditionals" : { 1979 | "active" : 1, 1980 | "flagValue" : -1 1981 | }, 1982 | "dead_code" : { 1983 | "active" : 1, 1984 | "flagValue" : -1 1985 | }, 1986 | "directives" : { 1987 | "active" : 1, 1988 | "flagValue" : -1 1989 | }, 1990 | "drop_console" : { 1991 | "active" : 0, 1992 | "flagValue" : -1 1993 | }, 1994 | "drop_debugger" : { 1995 | "active" : 1, 1996 | "flagValue" : -1 1997 | }, 1998 | "ecma" : { 1999 | "active" : 1, 2000 | "flagValue" : 5 2001 | }, 2002 | "eval" : { 2003 | "active" : 0, 2004 | "flagValue" : -1 2005 | }, 2006 | "evaluate" : { 2007 | "active" : 1, 2008 | "flagValue" : -1 2009 | }, 2010 | "expression" : { 2011 | "active" : 0, 2012 | "flagValue" : -1 2013 | }, 2014 | "hoist_funs" : { 2015 | "active" : 0, 2016 | "flagValue" : -1 2017 | }, 2018 | "hoist_props" : { 2019 | "active" : 1, 2020 | "flagValue" : -1 2021 | }, 2022 | "hoist_vars" : { 2023 | "active" : 0, 2024 | "flagValue" : -1 2025 | }, 2026 | "ie8" : { 2027 | "active" : 0, 2028 | "flagValue" : -1 2029 | }, 2030 | "if_return" : { 2031 | "active" : 1, 2032 | "flagValue" : -1 2033 | }, 2034 | "indent_level" : { 2035 | "active" : 0, 2036 | "flagValue" : 4 2037 | }, 2038 | "indent_start" : { 2039 | "active" : 0, 2040 | "flagValue" : 0 2041 | }, 2042 | "inline" : { 2043 | "active" : 1, 2044 | "flagValue" : 3 2045 | }, 2046 | "inline_script" : { 2047 | "active" : 1, 2048 | "flagValue" : -1 2049 | }, 2050 | "join_vars" : { 2051 | "active" : 1, 2052 | "flagValue" : -1 2053 | }, 2054 | "keep_classnames" : { 2055 | "active" : 0, 2056 | "flagValue" : -1 2057 | }, 2058 | "keep_fargs" : { 2059 | "active" : 1, 2060 | "flagValue" : -1 2061 | }, 2062 | "keep_fnames" : { 2063 | "active" : 0, 2064 | "flagValue" : -1 2065 | }, 2066 | "keep_infinity" : { 2067 | "active" : 0, 2068 | "flagValue" : -1 2069 | }, 2070 | "keep_numbers" : { 2071 | "active" : 0, 2072 | "flagValue" : -1 2073 | }, 2074 | "keep_quoted_props" : { 2075 | "active" : 0, 2076 | "flagValue" : -1 2077 | }, 2078 | "loops" : { 2079 | "active" : 1, 2080 | "flagValue" : -1 2081 | }, 2082 | "max_line_len" : { 2083 | "active" : 1, 2084 | "flagValue" : 32000 2085 | }, 2086 | "module" : { 2087 | "active" : 0, 2088 | "flagValue" : -1 2089 | }, 2090 | "negate_iife" : { 2091 | "active" : 1, 2092 | "flagValue" : -1 2093 | }, 2094 | "passes" : { 2095 | "active" : 1, 2096 | "flagValue" : 1 2097 | }, 2098 | "preserve_annotations" : { 2099 | "active" : 0, 2100 | "flagValue" : -1 2101 | }, 2102 | "properties" : { 2103 | "active" : 1, 2104 | "flagValue" : -1 2105 | }, 2106 | "pure_getters" : { 2107 | "active" : 0, 2108 | "flagValue" : -1 2109 | }, 2110 | "quote_keys" : { 2111 | "active" : 0, 2112 | "flagValue" : -1 2113 | }, 2114 | "quote_style" : { 2115 | "active" : 1, 2116 | "flagValue" : 0 2117 | }, 2118 | "reduce_funcs" : { 2119 | "active" : 1, 2120 | "flagValue" : -1 2121 | }, 2122 | "reduce_vars" : { 2123 | "active" : 1, 2124 | "flagValue" : -1 2125 | }, 2126 | "safari10" : { 2127 | "active" : 0, 2128 | "flagValue" : -1 2129 | }, 2130 | "semicolons" : { 2131 | "active" : 1, 2132 | "flagValue" : -1 2133 | }, 2134 | "sequences" : { 2135 | "active" : 1, 2136 | "flagValue" : -1 2137 | }, 2138 | "shebang" : { 2139 | "active" : 1, 2140 | "flagValue" : -1 2141 | }, 2142 | "side_effects" : { 2143 | "active" : 1, 2144 | "flagValue" : -1 2145 | }, 2146 | "switches" : { 2147 | "active" : 1, 2148 | "flagValue" : -1 2149 | }, 2150 | "toplevel" : { 2151 | "active" : 0, 2152 | "flagValue" : -1 2153 | }, 2154 | "typeofs" : { 2155 | "active" : 1, 2156 | "flagValue" : -1 2157 | }, 2158 | "unsafe" : { 2159 | "active" : 0, 2160 | "flagValue" : -1 2161 | }, 2162 | "unsafe_arrows" : { 2163 | "active" : 0, 2164 | "flagValue" : -1 2165 | }, 2166 | "unsafe_comps" : { 2167 | "active" : 0, 2168 | "flagValue" : -1 2169 | }, 2170 | "unsafe_Function" : { 2171 | "active" : 0, 2172 | "flagValue" : -1 2173 | }, 2174 | "unsafe_math" : { 2175 | "active" : 0, 2176 | "flagValue" : -1 2177 | }, 2178 | "unsafe_methods" : { 2179 | "active" : 0, 2180 | "flagValue" : -1 2181 | }, 2182 | "unsafe_proto" : { 2183 | "active" : 0, 2184 | "flagValue" : -1 2185 | }, 2186 | "unsafe_regexp" : { 2187 | "active" : 0, 2188 | "flagValue" : -1 2189 | }, 2190 | "unsafe_undefined" : { 2191 | "active" : 0, 2192 | "flagValue" : -1 2193 | }, 2194 | "unused" : { 2195 | "active" : 1, 2196 | "flagValue" : -1 2197 | }, 2198 | "warnings" : { 2199 | "active" : 0, 2200 | "flagValue" : -1 2201 | }, 2202 | "webkit" : { 2203 | "active" : 0, 2204 | "flagValue" : -1 2205 | }, 2206 | "wrap_func_args" : { 2207 | "active" : 1, 2208 | "flagValue" : -1 2209 | }, 2210 | "wrap_iife" : { 2211 | "active" : 0, 2212 | "flagValue" : -1 2213 | } 2214 | }, 2215 | "uglifyMangleNames" : 1, 2216 | "uglifyReservedNamesString" : "$,exports,require", 2217 | "webpPresets" : { 2218 | 2219 | }, 2220 | "websiteRelativeRoot" : "" 2221 | }, 2222 | "settingsFileVersion" : "3" 2223 | } -------------------------------------------------------------------------------- /docs/.sidebar.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Get Started", 4 | "collapsable": false, 5 | "children": [ 6 | "get-started/installation-setup", 7 | "get-started/requirements" 8 | ] 9 | }, 10 | { 11 | "title": "Feature Tour", 12 | "collapsable": false, 13 | "children": [ 14 | "feature-tour/usage" 15 | ] 16 | } 17 | ] -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verbb/many-to-many/29fe8634acdad31843f3e54167fb81fb30d3f2c4/docs/README.md -------------------------------------------------------------------------------- /docs/feature-tour/usage.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 1. Create your initial relationship field using the Entries Field Type and attach it to your first section. 3 | 2. Create another field that attaches the relationship using the Many to Many field type. 4 | * *Linked Section* will be the initial section that contains the relationship. (for example, Recipes). 5 | * *Associated Field* is the field on the other end of this relationship. (for example, Related Ingredients). 6 | 3. Attach the newly created Many to Many field to your section. 7 | 8 | ## Template Usage 9 | Since this plugin relies on Craft's built in relationships, you can continue to use relationships just as you always have. 10 | 11 | ```twig 12 |

Related Ingredients

13 | 14 | {% set relatedIngredients = craft.entries.section('ingredients').relatedTo(entry).all() %} 15 | 16 | {% for ingredient in relatedIngredients %} 17 | {{ ingredient.title }}
18 | {% endfor %} 19 | ``` 20 | 21 | ```twig 22 |

Related Recipes

23 | 24 | {% set relatedRecipes = craft.entries.section('recipes').relatedTo(entry).all() %} 25 | 26 | {% for recipe in relatedRecipes %} 27 | {{ recipe.title }}
28 | {% endfor %} 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/get-started/installation-setup.md: -------------------------------------------------------------------------------- 1 | # Installation & Setup 2 | You can install Many to Many via the plugin store, or through Composer. 3 | 4 | ## Craft Plugin Store 5 | To install **Many to Many**, navigate to the _Plugin Store_ section of your Craft control panel, search for `Many to Many`, and click the _Try_ button. 6 | 7 | ## Composer 8 | You can also add the package to your project using Composer and the command line. 9 | 10 | 1. Open your terminal and go to your Craft project: 11 | ```shell 12 | cd /path/to/project 13 | ``` 14 | 15 | 2. Then tell Composer to require the plugin, and Craft to install it: 16 | ```shell 17 | composer require verbb/many-to-many && php craft plugin/install manytomany 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/get-started/requirements.md: -------------------------------------------------------------------------------- 1 | # Requirements 2 | 3 | ## Craft CMS 4 | Many to Many requires Craft CMS 5.0 or greater. 5 | 6 | ## PHP 7 | Many to Many requires PHP 8.2 or greater. 8 | -------------------------------------------------------------------------------- /src/ManyToMany.php: -------------------------------------------------------------------------------- 1 | _registerFieldTypes(); 37 | } 38 | 39 | 40 | // Private Methods 41 | // ========================================================================= 42 | 43 | private function _registerFieldTypes(): void 44 | { 45 | Event::on(Fields::class, Fields::EVENT_REGISTER_FIELD_TYPES, function(RegisterComponentTypesEvent $event) { 46 | $event->types[] = ManyToManyField::class; 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/assetbundles/field/ManyToManyFieldAsset.php: -------------------------------------------------------------------------------- 1 | sourcePath = __DIR__ . '/dist'; 15 | 16 | $this->depends = [ 17 | CpAsset::class, 18 | ]; 19 | 20 | $this->js = [ 21 | 'many-to-many.js', 22 | ]; 23 | 24 | parent::init(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/assetbundles/field/dist/many-to-many.js: -------------------------------------------------------------------------------- 1 | void 0===Craft.ManyToMany&&(Craft.ManyToMany={}),function($){Craft.ManyToMany.Field=Garnish.Base.extend({elementsToDelete:[],init:function(e){this.id=e.id,this.name=e.name,this.elementIds=e.elementIds,this.$container=$("#"+e.id+"-field .js-mtm-field"),this.$deleteContainer=this.$container.find(".js-mtm-delete"),this.$elementSelect=this.$container.find(".js-mtm-element-select"),this.elementSelect=this.$elementSelect.data("elementSelect"),this.elementSelect.onSelectElements=$.proxy(this,"onSelectElements"),this.elementSelect.onRemoveElements=$.proxy(this,"onRemoveElements")},onSelectElements:function(e){const t=this;$.each(e,(function(e,n){var l=t.elementsToDelete.indexOf(n.id);-1!==l&&t.elementsToDelete.splice(l,1)}))},onRemoveElements:function(){const e=this;$.each(this.elementIds,(function(t,n){e.elementSelect.$elements.find('[data-id="'+n+'"]').length||e.elementsToDelete.push(n)})),this.updateDeletedElements()},updateDeletedElements:function(){const e=this;this.$deleteContainer.html(""),$.each(this.elementsToDelete,(function(t,n){let l='';e.$deleteContainer.append(l)}))}})}(jQuery); -------------------------------------------------------------------------------- /src/assetbundles/field/src/many-to-many.js: -------------------------------------------------------------------------------- 1 | // ========================================================================== 2 | 3 | // Many to Many Plugin for Craft CMS 4 | // Author: Verbb - https://verbb.io/ 5 | 6 | // ========================================================================== 7 | 8 | if (typeof Craft.ManyToMany === typeof undefined) { 9 | Craft.ManyToMany = {}; 10 | } 11 | 12 | (function($) { 13 | 14 | Craft.ManyToMany.Field = Garnish.Base.extend({ 15 | elementsToDelete: [], 16 | 17 | init: function(settings) { 18 | this.id = settings.id; 19 | this.name = settings.name; 20 | this.elementIds = settings.elementIds; 21 | this.$container = $('#' + settings.id + '-field .js-mtm-field'); 22 | this.$deleteContainer = this.$container.find('.js-mtm-delete'); 23 | this.$elementSelect = this.$container.find('.js-mtm-element-select'); 24 | this.elementSelect = this.$elementSelect.data('elementSelect'); 25 | 26 | this.elementSelect.onSelectElements = $.proxy(this, 'onSelectElements'); 27 | this.elementSelect.onRemoveElements = $.proxy(this, 'onRemoveElements'); 28 | }, 29 | 30 | onSelectElements: function(elements) { 31 | const self = this; 32 | 33 | $.each(elements, function(index, value) { 34 | var i = self.elementsToDelete.indexOf(value.id); 35 | 36 | if (i !== -1) { 37 | self.elementsToDelete.splice(i, 1); 38 | } 39 | }); 40 | }, 41 | 42 | onRemoveElements: function() { 43 | const self = this; 44 | 45 | $.each(this.elementIds, function(index, value) { 46 | if (!self.elementSelect.$elements.find('[data-id="' + value + '"]').length) { 47 | self.elementsToDelete.push(value); 48 | } 49 | }); 50 | 51 | this.updateDeletedElements(); 52 | }, 53 | 54 | updateDeletedElements: function() { 55 | const self = this; 56 | 57 | this.$deleteContainer.html(''); 58 | 59 | $.each(this.elementsToDelete, function(index, value) { 60 | let html = ''; 61 | 62 | self.$deleteContainer.append(html); 63 | }); 64 | }, 65 | }); 66 | 67 | })(jQuery); 68 | -------------------------------------------------------------------------------- /src/base/PluginTrait.php: -------------------------------------------------------------------------------- 1 | [ 33 | 'service' => Service::class, 34 | ], 35 | ]; 36 | } 37 | 38 | 39 | // Public Methods 40 | // ========================================================================= 41 | 42 | public function getService(): Service 43 | { 44 | return $this->get('service'); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /src/fields/ManyToManyField.php: -------------------------------------------------------------------------------- 1 | source['value'] ?? null; 68 | 69 | // Save the raw value for add/delete elements to use in `saveRelationship()`. We have to use the cache 70 | // as this isn't retained in `afterElementSave()`, and we want to wait until after the element has saved 71 | // to save the relationship, in case something went wrong with the element saving. 72 | $cacheKey = implode('--', ['many-to-many', $this->handle, $element->canonicalUid]); 73 | Craft::$app->getCache()->set($cacheKey, ($value ?? [])); 74 | 75 | if ($element && $sourceValue && $this->singleField) { 76 | $relatedSection = Craft::$app->getEntries()->getSectionByUid($sourceValue); 77 | 78 | // Get all the entries that this has already been attached to 79 | if ($relatedSection) { 80 | return ManyToMany::$plugin->getService()->getRelatedEntries($element, $relatedSection, $this->singleField); 81 | } 82 | } 83 | 84 | return $value; 85 | } 86 | 87 | public function getSettingsHtml(): string 88 | { 89 | $elements = []; 90 | $fields = []; 91 | 92 | // Group the Sections into an array 93 | foreach (Craft::$app->getEntries()->getAllSections() as $section) { 94 | $elements[$section->uid] = $section->name; 95 | } 96 | 97 | // Group Field Types into an array 98 | foreach (Craft::$app->getFields()->getAllFields() as $field) { 99 | $fields[$field->uid] = $field->name; 100 | } 101 | 102 | // Get the Section Source 103 | if (empty($this->source)) { 104 | $this->source = ['type' => '', 'value' => '']; 105 | } 106 | 107 | return Craft::$app->getView()->renderTemplate('manytomany/field/settings', [ 108 | 'field' => $this, 109 | 'elements' => $elements, 110 | 'fields' => $fields, 111 | ]); 112 | } 113 | 114 | public function afterElementSave(ElementInterface $element, bool $isNew): void 115 | { 116 | ManyToMany::$plugin->getService()->saveRelationship($this, $element); 117 | 118 | parent::afterElementSave($element, $isNew); 119 | } 120 | 121 | public function getPreviewHtml($value, ElementInterface $element): string 122 | { 123 | return Cp::elementPreviewHtml($value); 124 | } 125 | 126 | public function getContentGqlType(): array 127 | { 128 | return [ 129 | 'name' => $this->handle, 130 | 'type' => Type::nonNull(Type::listOf(EntryInterface::getType())), 131 | 'args' => EntryArguments::getArguments(), 132 | 'resolve' => function($source, $arguments, $context, $resolveInfo) { 133 | // Convert the already-resolved entries to an entries query. This is because `normalizeValue` 134 | // doesn't return the traditional EntryElementQuery value. 135 | $target = $source->{$this->handle}; 136 | $arguments['id'] = ArrayHelper::getColumn($target, 'id'); 137 | $arguments['siteId'] = $target[0]->siteId ?? null; 138 | 139 | return EntryResolver::resolve(null, $arguments, $context, $resolveInfo); 140 | }, 141 | 'complexity' => GqlHelper::relatedArgumentComplexity(GqlService::GRAPHQL_COMPLEXITY_EAGER_LOAD), 142 | ]; 143 | } 144 | 145 | 146 | // Protected Methods 147 | // ========================================================================= 148 | 149 | protected function inputHtml(mixed $value, ?ElementInterface $element, bool $inline): string 150 | { 151 | $view = Craft::$app->getView(); 152 | 153 | // Validate settings 154 | if (empty($this->source)) { 155 | return Craft::t('manytomany', 'To use the Many to Many plugin you need to set a source.'); 156 | } 157 | 158 | if (empty($this->singleField)) { 159 | return Craft::t('manytomany', 'To use the Many to Many plugin you need associate it with a related field.'); 160 | } 161 | 162 | $singleFieldModel = Craft::$app->getFields()->getFieldByUid($this->singleField); 163 | 164 | if ($singleFieldModel && $singleFieldModel->getIsTranslatable($element)) { 165 | return Craft::t('manytomany', 'The Many to Many plugin does not currently work with localized content.'); 166 | } 167 | 168 | // For this iteration of the plugin, everything is a SECTION, but it's setup, so it can be 169 | // refactored in the future to allow for multiple types 170 | if (!($element instanceof Entry)) { 171 | return Craft::t('manytomany', 'For this version of the Many to Many plugin, you can only use this field with Entries.'); 172 | } 173 | 174 | return $view->renderTemplate('manytomany/field/input', [ 175 | 'name' => $this->handle, 176 | 'value' => $value, 177 | 'id' => Html::id($this->handle), 178 | 'section' => $this->source['value'] ?? null, 179 | 'selectionLabel' => $this->selectionLabel ? Craft::t('site', $this->selectionLabel) : static::defaultSelectionLabel(), 180 | ]); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/icon-mask.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 10 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/services/Service.php: -------------------------------------------------------------------------------- 1 | section = $section; 37 | $query->limit = null; 38 | $query->status = null; 39 | $query->siteId = $element->siteId; 40 | $query->relatedTo = [ 41 | 'targetElement' => $element, 42 | 'field' => $fieldId, 43 | ]; 44 | 45 | return $query->all(); 46 | } 47 | 48 | /** 49 | * Save relationships on external field. 50 | * 51 | * @param ManyToManyField $fieldType 52 | * @param ElementInterface $element 53 | */ 54 | public function saveRelationship(ManyToManyField $fieldType, ElementInterface $element): void 55 | { 56 | if (ElementHelper::isDraftOrRevision($element)) { 57 | return; 58 | } 59 | 60 | $cacheKey = implode('--', ['many-to-many', $fieldType->handle, $element->canonicalUid]); 61 | $content = Craft::$app->getCache()->get($cacheKey); 62 | 63 | // Get element ID of the current element. Be sure to use the canonicalId when dealing with autosave 64 | $targetId = $element->getCanonicalId(); 65 | 66 | // Delete cache related to this element ID 67 | Craft::$app->getElements()->invalidateCachesForElement($element); 68 | 69 | // Get submitted field value - note that we're fetching `rawValue` not the normalized value 70 | Craft::$app->getCache()->delete($cacheKey); 71 | 72 | // There are 3 Items we need to make up a unique relationship in the craft_relations table: 73 | // fieldId --> We define this in the Field settings when creating it 74 | // sourceId --> The elementIds that create the relationship initially. This is currently stored in the $content array 75 | // targetId --> $elementId, this is the reverse of the relationship 76 | $fieldId = Db::idByUid(Table::FIELDS, $fieldType->singleField); 77 | 78 | $field = Craft::$app->fields->getFieldByUid($fieldType->singleField); 79 | 80 | // The relationships we either want to add or leave 81 | $toAdd = !empty($content['add']) ? $content['add'] : []; 82 | 83 | // The relationships we want to remove 84 | $toDelete = !empty($content['delete']) ? $content['delete'] : []; 85 | 86 | foreach ($toAdd as $sourceId) { 87 | $sourceElement = Entry::find()->id($sourceId)->one(); 88 | $currentRelations = array_map(fn($r) => $r->Id, $sourceElement->getFieldValue($field->handle)->all()); 89 | $currentRelations = array_unique(array_merge($currentRelations, [$targetId])); 90 | $sourceElement->setFieldValue($field->handle, $currentRelations); 91 | Craft::$app->elements->saveElement($sourceElement); 92 | } 93 | 94 | foreach ($toDelete as $sourceId) { 95 | $sourceElement = Entry::find()->id($sourceId)->one(); 96 | $currentRelations = array_map(fn($r) => $r->Id, $sourceElement->getFieldValue($field->handle)->all()); 97 | $currentRelations = array_filter($currentRelations, fn($r) => $r != $targetId); 98 | $sourceElement->setFieldValue($field->handle, $currentRelations); 99 | Craft::$app->elements->saveElement($sourceElement); 100 | } 101 | 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/templates/field/input.html: -------------------------------------------------------------------------------- 1 | {% import '_includes/forms' as forms %} 2 | 3 | {% do view.registerAssetBundle('verbb\\manytomany\\assetbundles\\field\\ManyToManyFieldAsset') %} 4 | 5 | {{ hiddenInput(name, '') }} 6 | 7 |
8 | {{ forms.elementSelect({ 9 | name: name ~ '[add]', 10 | elementType: 'craft\\elements\\Entry', 11 | sources: section ? ['section:' ~ section] : [], 12 | selectionLabel: selectionLabel, 13 | class: 'js-mtm-element-select', 14 | elements: value, 15 | sortable: false, 16 | }) }} 17 | 18 |
19 |
20 | 21 | {% set jsSettings = { 22 | id: id | namespaceInputId, 23 | name: name | namespaceInputName, 24 | elementIds: value | map(v => v.id), 25 | } %} 26 | 27 | {% js %} 28 | new Craft.ManyToMany.Field({{ jsSettings | json_encode | raw }}); 29 | {% endjs %} -------------------------------------------------------------------------------- /src/templates/field/settings.html: -------------------------------------------------------------------------------- 1 | {% import '_includes/forms' as forms %} 2 | 3 | {{ forms.selectField({ 4 | label: 'Linked Section' | t('manytomany'), 5 | instructions: 'What is the section you want to link in this relationship?' | t('manytomany'), 6 | id: 'source', 7 | name: 'source[value]', 8 | options: elements, 9 | value: field.source.value, 10 | errors: field.getErrors('source'), 11 | }) }} 12 | 13 | {{ forms.hidden({ 14 | name: 'source[type]', 15 | value: 'section', 16 | }) }} 17 | 18 | {{ forms.selectField({ 19 | label: 'Associated Field' | t('manytomany'), 20 | instructions: 'This is the field you are using to create the initial association. This has to be created first.' | t('manytomany'), 21 | id: 'singleField', 22 | name: 'singleField', 23 | options: fields, 24 | value: field.singleField, 25 | errors: field.getErrors('singleField'), 26 | }) }} 27 | 28 | {{ forms.textField({ 29 | label: 'Selection Label' | t('app'), 30 | instructions: 'Enter the text you want to appear on the selection input.' | t('app'), 31 | id: 'selectionLabel', 32 | name: 'selectionLabel', 33 | value: field.selectionLabel, 34 | placeholder: field.defaultSelectionLabel(), 35 | errors: field.getErrors('selectionLabel'), 36 | }) }} 37 | -------------------------------------------------------------------------------- /src/translations/en/manytomany.php: -------------------------------------------------------------------------------- 1 | 'Many to Many', 5 | 'Linked Section' => 'Linked Section', 6 | 'What is the section you want to link in this relationship?' => 'What is the section you want to link in this relationship?', 7 | 'Associated Field' => 'Associated Field', 8 | 'This is the field you are using to create the initial association. This has to be created first.' => 'This is the field you are using to create the initial association. This has to be created first.', 9 | 10 | 'For this version of the Many to Many plugin, you can only use this field with Entries.' => 'For this version of the Many to Many plugin, you can only use this field with Entries.', 11 | 'The Many to Many plugin does not currently work with localized content.' => 'The Many to Many plugin does not currently work with localized content.', 12 | 'To use the Many to Many plugin you need associate it with a related field.' => 'To use the Many to Many plugin you need associate it with a related field.', 13 | 'To use the Many to Many plugin you need to set a source.' => 'To use the Many to Many plugin you need to set a source.' 14 | ]; 15 | --------------------------------------------------------------------------------