├── .editorconfig ├── .eslintignore ├── .eslintrc.cjs ├── .github └── workflows │ └── release.yml ├── .gitignore ├── .prettierrc.cjs ├── .releaserc.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs ├── screenshot-dark.png └── screenshot-light.png ├── extension.config.mjs ├── package.json ├── pnpm-lock.yaml ├── src ├── composables │ ├── use-i18n.ts │ ├── use-permissions.ts │ └── use-relation-m2o.ts ├── index.ts ├── inline-form-m2o.vue ├── lang │ └── en.yaml ├── shims.d.ts ├── types │ ├── error.ts │ └── index.ts └── utils │ ├── is-allowed.ts │ └── unexpected-error.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root=true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = tab 8 | trim_trailing_whitespace = true 9 | 10 | [{package.json,*.yml,*.yaml}] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [Dockerfile] 15 | indent_style = tab 16 | 17 | [Makefile] 18 | indent_style = tab 19 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | const defaultRules = { 2 | // No console statements in production 3 | 'no-console': process.env.NODE_ENV !== 'development' ? 'error' : 'off', 4 | // No debugger statements in production 5 | 'no-debugger': process.env.NODE_ENV !== 'development' ? 'error' : 'off', 6 | // Enforce prettier formatting 7 | 'prettier/prettier': 'error', 8 | }; 9 | 10 | module.exports = { 11 | // Stop looking for ESLint configurations in parent folders 12 | root: true, 13 | // Global variables: Browser and Node.js 14 | env: { 15 | browser: true, 16 | node: true, 17 | }, 18 | // Basic configuration for js files 19 | plugins: ['@typescript-eslint', 'prettier'], 20 | extends: ['eslint:recommended', 'prettier'], 21 | rules: defaultRules, 22 | parserOptions: { 23 | ecmaVersion: 2020, 24 | }, 25 | overrides: [ 26 | // Parse rollup configration as module 27 | { 28 | files: ['rollup.config.js', 'vite.config.js'], 29 | parserOptions: { 30 | sourceType: 'module', 31 | }, 32 | }, 33 | // Configuration for ts/vue files 34 | { 35 | files: ['*.ts', '*.vue'], 36 | parser: 'vue-eslint-parser', 37 | parserOptions: { 38 | parser: '@typescript-eslint/parser', 39 | }, 40 | extends: [ 41 | 'plugin:vue/vue3-recommended', 42 | 'eslint:recommended', 43 | 'plugin:@typescript-eslint/recommended', 44 | 'prettier', 45 | ], 46 | rules: { 47 | ...defaultRules, 48 | // It's recommended to turn off this rule on TypeScript projects 49 | 'no-undef': 'off', 50 | // Allow ts-directive comments (used to suppress TypeScript compiler errors) 51 | '@typescript-eslint/ban-ts-comment': 'off', 52 | // Allow usage of the any type (consider to enable this rule later on) 53 | '@typescript-eslint/no-explicit-any': 'off', 54 | // Allow usage of require statements (consider to enable this rule later on) 55 | '@typescript-eslint/no-var-requires': 'off', 56 | // Allow non-null assertions for now (consider to enable this rule later on) 57 | '@typescript-eslint/no-non-null-assertion': 'off', 58 | // Allow unused arguments and variables when they begin with an underscore 59 | '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], 60 | }, 61 | }, 62 | ], 63 | }; 64 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: [push, workflow_dispatch] 3 | jobs: 4 | release: 5 | name: Release 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout 9 | uses: actions/checkout@v3 10 | with: 11 | fetch-depth: 0 12 | 13 | - name: Install pnpm 14 | uses: pnpm/action-setup@v2 15 | 16 | - name: Setup Node.js 17 | uses: actions/setup-node@v3 18 | with: 19 | node-version: lts/* 20 | cache: 'pnpm' 21 | 22 | - name: Install dependencies 23 | run: pnpm install --frozen-lockfile 24 | 25 | - name: Build 26 | run: pnpm build 27 | 28 | - name: Release 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 32 | run: pnpm release 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | .idea 5 | 6 | extension.config.cjs 7 | -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | htmlWhitespaceSensitivity: 'ignore', 3 | printWidth: 120, 4 | singleQuote: true, 5 | useTabs: true, 6 | proseWrap: 'always', 7 | overrides: [ 8 | { 9 | files: ['*.yaml', '*.yml', 'package.json'], 10 | options: { 11 | useTabs: false, 12 | tabWidth: 2, 13 | }, 14 | }, 15 | { 16 | files: ['Dockerfile', 'Makefile'], 17 | options: { 18 | useTabs: true, 19 | }, 20 | }, 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "+([0-9])?(.{+([0-9]),x}).x", 4 | "main", 5 | "next", 6 | "next-major", 7 | { 8 | "name": "beta", 9 | "prerelease": true 10 | }, 11 | { 12 | "name": "alpha", 13 | "prerelease": true 14 | } 15 | ], 16 | "plugins": [ 17 | "@semantic-release/commit-analyzer", 18 | "@semantic-release/release-notes-generator", 19 | "@semantic-release/changelog", 20 | "@semantic-release/npm", 21 | "@semantic-release/git", 22 | "@semantic-release/github" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [1.2.0](https://github.com/hanneskuettner/directus-extension-inline-form-interface/compare/v1.1.0...v1.2.0) (2024-03-07) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * Permissions compatibility with Directus 10.9.1+ ([cc356d2](https://github.com/hanneskuettner/directus-extension-inline-form-interface/commit/cc356d2b964d5b6acf979105281dc20a1317e8e2)) 7 | 8 | 9 | ### Features 10 | 11 | * Bump dependencies ([7ed43ea](https://github.com/hanneskuettner/directus-extension-inline-form-interface/commit/7ed43eaca184f647e70296e325b253cee39fb6ad)) 12 | 13 | # [1.1.0](https://github.com/hanneskuettner/directus-extension-inline-form-interface/compare/v1.0.1...v1.1.0) (2023-05-09) 14 | 15 | 16 | ### Features 17 | 18 | * **license:** change license to GPL-3.0 ([96def50](https://github.com/hanneskuettner/directus-extension-inline-form-interface/commit/96def50cad45484f1334e25b0868bc82fc42cc9d)) 19 | 20 | ## [1.0.1](https://github.com/hanneskuettner/directus-extension-inline-form-interface/compare/v1.0.0...v1.0.1) (2023-05-04) 21 | 22 | 23 | ### Bug Fixes 24 | 25 | * only include dist in package contents ([8cb9541](https://github.com/hanneskuettner/directus-extension-inline-form-interface/commit/8cb9541e9d1b2571d00324d53e92ac34cf8ecbcb)) 26 | 27 | # 1.0.0 (2023-05-04) 28 | 29 | 30 | ### Bug Fixes 31 | 32 | * install correct semantic-release plugin ([4dc87a0](https://github.com/hanneskuettner/directus-extension-inline-form-interface/commit/4dc87a0d106af4d973f6247219d8b2d9fa756b59)) 33 | 34 | 35 | ### Features 36 | 37 | * first release ([68abc21](https://github.com/hanneskuettner/directus-extension-inline-form-interface/commit/68abc2159f52f84bd8a565d4d5cffce8f7d555f2)) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute 5 | verbatim copies of this license document, but changing it is not allowed. 6 | 7 | Preamble 8 | 9 | The GNU General Public License is a free, copyleft license for software and other kinds of works. 10 | 11 | The licenses for most software and other practical works are designed to take away your freedom to share and change the 12 | works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all 13 | versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use 14 | the GNU General Public License for most of our software; it applies also to any other work released this way by its 15 | authors. You can apply it to your programs, too. 16 | 17 | When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make 18 | sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive 19 | source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and 20 | that you know you can do these things. 21 | 22 | To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. 23 | Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: 24 | responsibilities to respect the freedom of others. 25 | 26 | For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients 27 | the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must 28 | show them these terms so they know their rights. 29 | 30 | Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer 31 | you this License giving you legal permission to copy, distribute and/or modify it. 32 | 33 | For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. 34 | For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems 35 | will not be attributed erroneously to authors of previous versions. 36 | 37 | Some devices are designed to deny users access to install or run modified versions of the software inside them, although 38 | the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the 39 | software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely 40 | where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those 41 | products. If such problems arise substantially in other domains, we stand ready to extend this provision to those 42 | domains in future versions of the GPL, as needed to protect the freedom of users. 43 | 44 | Finally, every program is threatened constantly by software patents. States should not allow patents to restrict 45 | development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger 46 | that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that 47 | patents cannot be used to render the program non-free. 48 | 49 | The precise terms and conditions for copying, distribution and modification follow. 50 | 51 | TERMS AND CONDITIONS 52 | 53 | 0. Definitions. 54 | 55 | "This License" refers to version 3 of the GNU General Public License. 56 | 57 | "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. 58 | 59 | "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". 60 | "Licensees" and "recipients" may be individuals or organizations. 61 | 62 | To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, 63 | other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work 64 | "based on" the earlier work. 65 | 66 | A "covered work" means either the unmodified Program or a work based on the Program. 67 | 68 | To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily 69 | liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. 70 | Propagation includes copying, distribution (with or without modification), making available to the public, and in some 71 | countries other activities as well. 72 | 73 | To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction 74 | with a user through a computer network, with no transfer of a copy, is not conveying. 75 | 76 | An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and 77 | prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no 78 | warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this 79 | License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a 80 | menu, a prominent item in the list meets this criterion. 81 | 82 | 1. Source Code. 83 | 84 | The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means 85 | any non-source form of a work. 86 | 87 | A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, 88 | or, in the case of interfaces specified for a particular programming language, one that is widely used among developers 89 | working in that language. 90 | 91 | The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in 92 | the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to 93 | enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is 94 | available to the public in source code form. A "Major Component", in this context, means a major essential component 95 | (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a 96 | compiler used to produce the work, or an object code interpreter used to run it. 97 | 98 | The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and 99 | (for an executable work) run the object code and to modify the work, including scripts to control those activities. 100 | However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs 101 | which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding 102 | Source includes interface definition files associated with source files for the work, and the source code for shared 103 | libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data 104 | communication or control flow between those subprograms and other parts of the work. 105 | 106 | The Corresponding Source need not include anything that users can regenerate automatically from other parts of the 107 | Corresponding Source. 108 | 109 | The Corresponding Source for a work in source code form is that same work. 110 | 111 | 2. Basic Permissions. 112 | 113 | All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided 114 | the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. 115 | The output from running a covered work is covered by this License only if the output, given its content, constitutes a 116 | covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. 117 | 118 | You may make, run and propagate covered works that you do not convey, without conditions so long as your license 119 | otherwise remains in force. You may convey covered works to others for the sole purpose of having them make 120 | modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with 121 | the terms of this License in conveying all material for which you do not control copyright. Those thus making or running 122 | the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that 123 | prohibit them from making any copies of your copyrighted material outside their relationship with you. 124 | 125 | Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not 126 | allowed; section 10 makes it unnecessary. 127 | 128 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 129 | 130 | No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling 131 | obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or 132 | restricting circumvention of such measures. 133 | 134 | When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the 135 | extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you 136 | disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, 137 | your or third parties' legal rights to forbid circumvention of technological measures. 138 | 139 | 4. Conveying Verbatim Copies. 140 | 141 | You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you 142 | conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating 143 | that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices 144 | of the absence of any warranty; and give all recipients a copy of this License along with the Program. 145 | 146 | You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for 147 | a fee. 148 | 149 | 5. Conveying Modified Source Versions. 150 | 151 | You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source 152 | code under the terms of section 4, provided that you also meet all of these conditions: 153 | 154 | a) The work must carry prominent notices stating that you modified 155 | it, and giving a relevant date. 156 | 157 | b) The work must carry prominent notices stating that it is 158 | released under this License and any conditions added under section 159 | 7. This requirement modifies the requirement in section 4 to 160 | "keep intact all notices". 161 | 162 | c) You must license the entire work, as a whole, under this 163 | License to anyone who comes into possession of a copy. This 164 | License will therefore apply, along with any applicable section 7 165 | additional terms, to the whole of the work, and all its parts, 166 | regardless of how they are packaged. This License gives no 167 | permission to license the work in any other way, but it does not 168 | invalidate such permission if you have separately received it. 169 | 170 | d) If the work has interactive user interfaces, each must display 171 | Appropriate Legal Notices; however, if the Program has interactive 172 | interfaces that do not display Appropriate Legal Notices, your 173 | work need not make them do so. 174 | 175 | A compilation of a covered work with other separate and independent works, which are not by their nature extensions of 176 | the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or 177 | distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the 178 | access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work 179 | in an aggregate does not cause this License to apply to the other parts of the aggregate. 180 | 181 | 6. Conveying Non-Source Forms. 182 | 183 | You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the 184 | machine-readable Corresponding Source under the terms of this License, in one of these ways: 185 | 186 | a) Convey the object code in, or embodied in, a physical product 187 | (including a physical distribution medium), accompanied by the 188 | Corresponding Source fixed on a durable physical medium 189 | customarily used for software interchange. 190 | 191 | b) Convey the object code in, or embodied in, a physical product 192 | (including a physical distribution medium), accompanied by a 193 | written offer, valid for at least three years and valid for as 194 | long as you offer spare parts or customer support for that product 195 | model, to give anyone who possesses the object code either (1) a 196 | copy of the Corresponding Source for all the software in the 197 | product that is covered by this License, on a durable physical 198 | medium customarily used for software interchange, for a price no 199 | more than your reasonable cost of physically performing this 200 | conveying of source, or (2) access to copy the 201 | Corresponding Source from a network server at no charge. 202 | 203 | c) Convey individual copies of the object code with a copy of the 204 | written offer to provide the Corresponding Source. This 205 | alternative is allowed only occasionally and noncommercially, and 206 | only if you received the object code with such an offer, in accord 207 | with subsection 6b. 208 | 209 | d) Convey the object code by offering access from a designated 210 | place (gratis or for a charge), and offer equivalent access to the 211 | Corresponding Source in the same way through the same place at no 212 | further charge. You need not require recipients to copy the 213 | Corresponding Source along with the object code. If the place to 214 | copy the object code is a network server, the Corresponding Source 215 | may be on a different server (operated by you or a third party) 216 | that supports equivalent copying facilities, provided you maintain 217 | clear directions next to the object code saying where to find the 218 | Corresponding Source. Regardless of what server hosts the 219 | Corresponding Source, you remain obligated to ensure that it is 220 | available for as long as needed to satisfy these requirements. 221 | 222 | e) Convey the object code using peer-to-peer transmission, provided 223 | you inform other peers where the object code and Corresponding 224 | Source of the work are being offered to the general public at no 225 | charge under subsection 6d. 226 | 227 | A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, 228 | need not be included in conveying the object code work. 229 | 230 | A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used 231 | for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In 232 | determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a 233 | particular product received by a particular user, "normally used" refers to a typical or common use of that class of 234 | product, regardless of the status of the particular user or of the way in which the particular user actually uses, or 235 | expects or is expected to use, the product. A product is a consumer product regardless of whether the product has 236 | substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of 237 | the product. 238 | 239 | "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information 240 | required to install and execute modified versions of a covered work in that User Product from a modified version of its 241 | Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code 242 | is in no case prevented or interfered with solely because modification has been made. 243 | 244 | If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the 245 | conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to 246 | the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding 247 | Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not 248 | apply if neither you nor any third party retains the ability to install modified object code on the User Product (for 249 | example, the work has been installed in ROM). 250 | 251 | The requirement to provide Installation Information does not include a requirement to continue to provide support 252 | service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product 253 | in which it has been modified or installed. Access to a network may be denied when the modification itself materially 254 | and adversely affects the operation of the network or violates the rules and protocols for communication across the 255 | network. 256 | 257 | Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format 258 | that is publicly documented (and with an implementation available to the public in source code form), and must require 259 | no special password or key for unpacking, reading or copying. 260 | 261 | 7. Additional Terms. 262 | 263 | "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of 264 | its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were 265 | included in this License, to the extent that they are valid under applicable law. If additional permissions apply only 266 | to part of the Program, that part may be used separately under those permissions, but the entire Program remains 267 | governed by this License without regard to the additional permissions. 268 | 269 | When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or 270 | from any part of it. (Additional permissions may be written to require their own removal in certain cases when you 271 | modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have 272 | or can give appropriate copyright permission. 273 | 274 | Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by 275 | the copyright holders of that material) supplement the terms of this License with terms: 276 | 277 | a) Disclaiming warranty or limiting liability differently from the 278 | terms of sections 15 and 16 of this License; or 279 | 280 | b) Requiring preservation of specified reasonable legal notices or 281 | author attributions in that material or in the Appropriate Legal 282 | Notices displayed by works containing it; or 283 | 284 | c) Prohibiting misrepresentation of the origin of that material, or 285 | requiring that modified versions of such material be marked in 286 | reasonable ways as different from the original version; or 287 | 288 | d) Limiting the use for publicity purposes of names of licensors or 289 | authors of the material; or 290 | 291 | e) Declining to grant rights under trademark law for use of some 292 | trade names, trademarks, or service marks; or 293 | 294 | f) Requiring indemnification of licensors and authors of that 295 | material by anyone who conveys the material (or modified versions of 296 | it) with contractual assumptions of liability to the recipient, for 297 | any liability that these contractual assumptions directly impose on 298 | those licensors and authors. 299 | 300 | All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the 301 | Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with 302 | a term that is a further restriction, you may remove that term. If a license document contains a further restriction but 303 | permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of 304 | that license document, provided that the further restriction does not survive such relicensing or conveying. 305 | 306 | If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a 307 | statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. 308 | 309 | Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as 310 | exceptions; the above requirements apply either way. 311 | 312 | 8. Termination. 313 | 314 | You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to 315 | propagate or modify it is void, and will automatically terminate your rights under this License (including any patent 316 | licenses granted under the third paragraph of section 11). 317 | 318 | However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated 319 | (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) 320 | permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days 321 | after the cessation. 322 | 323 | Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you 324 | of the violation by some reasonable means, this is the first time you have received notice of violation of this License 325 | (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. 326 | 327 | Termination of your rights under this section does not terminate the licenses of parties who have received copies or 328 | rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not 329 | qualify to receive new licenses for the same material under section 10. 330 | 331 | 9. Acceptance Not Required for Having Copies. 332 | 333 | You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a 334 | covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not 335 | require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered 336 | work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a 337 | covered work, you indicate your acceptance of this License to do so. 338 | 339 | 10. Automatic Licensing of Downstream Recipients. 340 | 341 | Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, 342 | modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third 343 | parties with this License. 344 | 345 | An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or 346 | subdividing an organization, or merging organizations. If propagation of a covered work results from an entity 347 | transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work 348 | the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the 349 | Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with 350 | reasonable efforts. 351 | 352 | You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For 353 | example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, 354 | and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent 355 | claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 356 | 357 | 11. Patents. 358 | 359 | A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the 360 | Program is based. The work thus licensed is called the contributor's "contributor version". 361 | 362 | A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already 363 | acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or 364 | selling its contributor version, but do not include claims that would be infringed only as a consequence of further 365 | modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent 366 | sublicenses in a manner consistent with the requirements of this License. 367 | 368 | Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential 369 | patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its 370 | contributor version. 371 | 372 | In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not 373 | to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). 374 | To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent 375 | against the party. 376 | 377 | If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not 378 | available for anyone to copy, free of charge and under the terms of this License, through a publicly available network 379 | server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or 380 | (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a 381 | manner consistent with the requirements of this License, to extend the patent license to downstream recipients. 382 | "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in 383 | a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in 384 | that country that you have reason to believe are valid. 385 | 386 | If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring 387 | conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing 388 | them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is 389 | automatically extended to all recipients of the covered work and works based on it. 390 | 391 | A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, 392 | or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You 393 | may not convey a covered work if you are a party to an arrangement with a third party that is in the business of 394 | distributing software, under which you make payment to the third party based on the extent of your activity of conveying 395 | the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a 396 | discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from 397 | those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered 398 | work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. 399 | 400 | Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to 401 | infringement that may otherwise be available to you under applicable patent law. 402 | 403 | 12. No Surrender of Others' Freedom. 404 | 405 | If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this 406 | License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to 407 | satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence 408 | you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further 409 | conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License 410 | would be to refrain entirely from conveying the Program. 411 | 412 | 13. Use with the GNU Affero General Public License. 413 | 414 | Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work 415 | licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the 416 | resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special 417 | requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply 418 | to the combination as such. 419 | 420 | 14. Revised Versions of this License. 421 | 422 | The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to 423 | time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new 424 | problems or concerns. 425 | 426 | Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the 427 | GNU General Public License "or any later version" applies to it, you have the option of following the terms and 428 | conditions either of that numbered version or of any later version published by the Free Software Foundation. If the 429 | Program does not specify a version number of the GNU General Public License, you may choose any version ever published 430 | by the Free Software Foundation. 431 | 432 | If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, 433 | that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the 434 | Program. 435 | 436 | Later license versions may give you additional or different permissions. However, no additional obligations are imposed 437 | on any author or copyright holder as a result of your choosing to follow a later version. 438 | 439 | 15. Disclaimer of Warranty. 440 | 441 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING 442 | THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR 443 | IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 444 | THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU 445 | ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. Limitation of Liability. 448 | 449 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO 450 | MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, 451 | INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO 452 | LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM 453 | TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 454 | DAMAGES. 455 | 456 | 17. Interpretation of Sections 15 and 16. 457 | 458 | If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to 459 | their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil 460 | liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program 461 | in return for a fee. 462 | 463 | END OF TERMS AND CONDITIONS 464 | 465 | How to Apply These Terms to Your New Programs 466 | 467 | If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve 468 | this is to make it free software which everyone can redistribute and change under these terms. 469 | 470 | To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to 471 | most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer 472 | to where the full notice is found. 473 | 474 | 475 | Copyright (C) 476 | 477 | This program is free software: you can redistribute it and/or modify 478 | it under the terms of the GNU General Public License as published by 479 | the Free Software Foundation, either version 3 of the License, or 480 | (at your option) any later version. 481 | 482 | This program is distributed in the hope that it will be useful, 483 | but WITHOUT ANY WARRANTY; without even the implied warranty of 484 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 485 | GNU General Public License for more details. 486 | 487 | You should have received a copy of the GNU General Public License 488 | along with this program. If not, see . 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: 493 | 494 | Copyright (C) 495 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 496 | This is free software, and you are welcome to redistribute it 497 | under certain conditions; type `show c' for details. 498 | 499 | The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of 500 | course, your program's commands might be different; for a GUI interface, you would use an "about box". 501 | 502 | You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for 503 | the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see 504 | . 505 | 506 | The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is 507 | a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If 508 | this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read 509 | . 510 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Directus Inline Form Interface 2 | 3 | An inline form interface for Directus that allows editing of a related M2O collection 4 | item within its parent's form. 5 | 6 | For more details as to why you would need this see these discussions in the Directus repository 7 | [directus/directus#3474](https://github.com/directus/directus/discussions/3474) and 8 | [directus/directus#18041](https://github.com/directus/directus/discussions/18041). 9 | 10 | 11 | 12 | Screenshot of the tab group interface 13 | 14 | 15 | ## Installation 16 | 17 | Add `directus-extension-inline-form-interface` to your project: 18 | 19 | 20 | ```shell 21 | # Using pnpm 22 | pnpm add directus-extension-inline-form-interface 23 | # Using yarn 24 | yarn add directus-extension-inline-form-interface 25 | # Using npm 26 | npm install directus-extension-inline-form-interface 27 | ``` 28 | 29 | ## Usage 30 | 31 | When creating a new M2O field you can select `Inline Form` in the `Relational` section. 32 | 33 | Alternatively you can change the interface of an existing M2O field in the 34 | `Interface` section. 35 | 36 | ## Options 37 | 38 | #### `Create Related Item` 39 | 40 | Decide when an item should be created in the related collection. 41 | 42 | You can choose between: 43 | 44 | - `Only with Content`, which will only create a new item if *some* content is filled in the inline form, and 45 | - `Always`, which will always create a new item, even if no content is filled in the inline form 46 | -------------------------------------------------------------------------------- /docs/screenshot-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanneskuettner/directus-extension-inline-form-interface/bcfad72aedc11f91dd6fab344d472cdfa130c933/docs/screenshot-dark.png -------------------------------------------------------------------------------- /docs/screenshot-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanneskuettner/directus-extension-inline-form-interface/bcfad72aedc11f91dd6fab344d472cdfa130c933/docs/screenshot-light.png -------------------------------------------------------------------------------- /extension.config.mjs: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { fileURLToPath } from 'node:url'; 3 | import alias from '@rollup/plugin-alias'; 4 | import resolve from '@rollup/plugin-node-resolve'; 5 | import yaml from "@rollup/plugin-yaml"; 6 | 7 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 8 | const rootDir = path.resolve(__dirname); 9 | const customResolver = resolve({ 10 | extensions: ['.ts', '.js'] 11 | }); 12 | 13 | export default{ 14 | plugins: [ 15 | alias({ 16 | customResolver, 17 | entries: [ 18 | {find: /^@\/(.*)/, replacement: path.resolve(rootDir, 'src/$1')} 19 | ] 20 | }), 21 | yaml() 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "directus-extension-inline-form-interface", 3 | "description": "An inline form interface for Directus", 4 | "icon": "extension", 5 | "version": "1.2.0", 6 | "license": "GPL-3.0", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/hanneskuettner/directus-extension-inline-form-interface.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/hanneskuettner/directus-extension-inline-form-interface/issues" 13 | }, 14 | "homepage": "https://github.com/hanneskuettner/directus-extension-inline-form-interface", 15 | "keywords": [ 16 | "directus", 17 | "directus-extension", 18 | "directus-custom-interface" 19 | ], 20 | "directus:extension": { 21 | "type": "interface", 22 | "path": "dist/index.js", 23 | "source": "src/index.ts", 24 | "host": "^10.9.0" 25 | }, 26 | "files": [ 27 | "dist" 28 | ], 29 | "scripts": { 30 | "build": "directus-extension build", 31 | "dev": "directus-extension build -w --no-minify", 32 | "link": "directus-extension link", 33 | "lint": "eslint .", 34 | "release": "semantic-release" 35 | }, 36 | "devDependencies": { 37 | "@directus/composables": "^10.1.11", 38 | "@directus/exceptions": "^10.0.3", 39 | "@directus/types": "^11.0.7", 40 | "@directus/utils": "^11.0.6", 41 | "@rollup/plugin-alias": "^5.1.0", 42 | "@rollup/plugin-node-resolve": "^15.2.3", 43 | "@rollup/plugin-yaml": "^4.1.2", 44 | "@semantic-release/changelog": "^6.0.3", 45 | "@semantic-release/git": "^10.0.1", 46 | "@types/lodash-es": "^4.17.12", 47 | "@typescript-eslint/eslint-plugin": "^7.1.1", 48 | "@typescript-eslint/parser": "^7.1.1", 49 | "eslint": "^8.57.0", 50 | "eslint-config-prettier": "^9.1.0", 51 | "eslint-plugin-prettier": "^5.1.3", 52 | "eslint-plugin-vue": "^9.22.0", 53 | "knex-schema-inspector": "^3.1.0", 54 | "lodash-es": "^4.17.21", 55 | "prettier": "^3.2.5", 56 | "sass": "^1.71.1", 57 | "semantic-release": "^23.0.2", 58 | "typescript": "^5.4.2", 59 | "@directus/extensions-sdk": "^11.0.1", 60 | "vue": "^3.2.47", 61 | "vue-i18n": "^9.2.2" 62 | }, 63 | "publishConfig": { 64 | "access": "public" 65 | }, 66 | "packageManager": "pnpm@8.15.4" 67 | } 68 | -------------------------------------------------------------------------------- /src/composables/use-i18n.ts: -------------------------------------------------------------------------------- 1 | import en from '@/lang/en.yaml'; 2 | import { useI18n as origI18n, UseI18nOptions } from 'vue-i18n'; 3 | 4 | export function useI18n(options?: UseI18nOptions) { 5 | return origI18n({ 6 | ...options, 7 | messages: { 8 | en, 9 | }, 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /src/composables/use-permissions.ts: -------------------------------------------------------------------------------- 1 | import { isAllowed } from '@/utils/is-allowed'; 2 | import { useCollection } from '@directus/composables'; 3 | import { useStores } from '@directus/extensions-sdk'; 4 | import { Field } from '@directus/types'; 5 | import { cloneDeep } from 'lodash-es'; 6 | import { computed, ComputedRef, Ref } from 'vue'; 7 | 8 | type UsablePermissions = { 9 | createAllowed: ComputedRef; 10 | deleteAllowed: ComputedRef; 11 | saveAllowed: ComputedRef; 12 | archiveAllowed: ComputedRef; 13 | updateAllowed: ComputedRef; 14 | shareAllowed: ComputedRef; 15 | fields: ComputedRef; 16 | revisionsAllowed: ComputedRef; 17 | }; 18 | 19 | export function usePermissions(collection: Ref, item: Ref, isNew: Ref): UsablePermissions { 20 | const { useUserStore, usePermissionsStore } = useStores(); 21 | const userStore = useUserStore(); 22 | const permissionsStore = usePermissionsStore(); 23 | 24 | const { info: collectionInfo, fields: rawFields } = useCollection(collection); 25 | 26 | const createAllowed = computed(() => isAllowed(collection.value, 'create', item.value)); 27 | 28 | const deleteAllowed = computed(() => isAllowed(collection.value, 'delete', item.value)); 29 | 30 | const saveAllowed = computed(() => { 31 | if (isNew.value) { 32 | return true; 33 | } 34 | 35 | return isAllowed(collection.value, 'update', item.value); 36 | }); 37 | 38 | const updateAllowed = computed(() => isAllowed(collection.value, 'update', item.value)); 39 | 40 | const shareAllowed = computed(() => isAllowed(collection.value, 'share', item.value)); 41 | 42 | const archiveAllowed = computed(() => { 43 | if (!collectionInfo.value?.meta?.archive_field) return false; 44 | 45 | return isAllowed( 46 | collection.value, 47 | 'update', 48 | { 49 | [collectionInfo.value.meta.archive_field]: collectionInfo.value.meta.archive_value, 50 | }, 51 | true 52 | ); 53 | }); 54 | 55 | const fields = computed(() => { 56 | let fields = cloneDeep(rawFields.value); 57 | 58 | if (userStore.currentUser?.role?.admin_access === true) return fields; 59 | 60 | const permissions = permissionsStore.getPermission(collection.value, isNew.value ? 'create' : 'update'); 61 | 62 | // remove fields without read permissions so they don't show up in the DOM 63 | const readableFields = permissionsStore.getPermission(collection.value, 'read')?.fields; 64 | if (readableFields && readableFields.includes('*') === false) { 65 | fields = fields.filter((field) => readableFields.includes(field.field)); 66 | } 67 | 68 | if (!permissions) return fields; 69 | 70 | if (permissions.fields?.includes('*') === false) { 71 | fields = fields.map((field: Field) => { 72 | if (permissions.fields?.includes(field.field) === false) { 73 | field.meta = { 74 | ...(field.meta || {}), 75 | readonly: true, 76 | } as any; 77 | } 78 | 79 | return field; 80 | }); 81 | } 82 | 83 | if (permissions.presets) { 84 | fields = fields.map((field: Field) => { 85 | if (field.field in permissions.presets!) { 86 | field.schema = { 87 | ...(field.schema || {}), 88 | default_value: permissions.presets![field.field], 89 | } as any; 90 | } 91 | 92 | return field; 93 | }); 94 | } 95 | 96 | return fields; 97 | }); 98 | 99 | const revisionsAllowed = computed(() => { 100 | if (userStore.currentUser?.role?.admin_access === true) return true; 101 | return !!permissionsStore.permissions.find( 102 | (permission) => permission.collection === 'directus_revisions' && permission.action === 'read' 103 | ); 104 | }); 105 | 106 | return { 107 | createAllowed, 108 | deleteAllowed, 109 | saveAllowed, 110 | archiveAllowed, 111 | updateAllowed, 112 | shareAllowed, 113 | fields, 114 | revisionsAllowed, 115 | }; 116 | } 117 | -------------------------------------------------------------------------------- /src/composables/use-relation-m2o.ts: -------------------------------------------------------------------------------- 1 | import { useStores } from '@directus/extensions-sdk'; 2 | import { Collection, Field, Relation } from '@directus/types'; 3 | import { computed, Ref } from 'vue'; 4 | 5 | export type RelationM2O = { 6 | relation: Relation; 7 | relatedCollection: Collection; 8 | relatedPrimaryKeyField: Field; 9 | type: 'm2o'; 10 | }; 11 | 12 | /* 13 | One Many: relatedCollection 14 | ┌──────────┐ ┌───────────────────┐ 15 | │id │ ┌────┤id: relatedPKField │ 16 | │many_id │◄───┘ │ │ 17 | └──────────┘ └───────────────────┘ 18 | */ 19 | 20 | export function useRelationM2O(collection: Ref, field: Ref) { 21 | const { useRelationsStore, useCollectionsStore, useFieldsStore } = useStores(); 22 | const relationsStore = useRelationsStore(); 23 | const collectionsStore = useCollectionsStore(); 24 | const fieldsStore = useFieldsStore(); 25 | 26 | const relationInfo = computed(() => { 27 | const relations = relationsStore.getRelationsForField(collection.value, field.value); 28 | 29 | if (relations.length === 0) return undefined; 30 | 31 | const relation = relations[0]; 32 | 33 | return { 34 | relation: relation, 35 | relatedCollection: collectionsStore.getCollection(relation.related_collection as string), 36 | relatedPrimaryKeyField: fieldsStore.getPrimaryKeyFieldForCollection(relation.related_collection as string), 37 | type: 'm2o', 38 | } as RelationM2O; 39 | }); 40 | 41 | return { relationInfo }; 42 | } 43 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { defineInterface } from '@directus/extensions-sdk'; 2 | import InterfaceInlineFormM2O from './inline-form-m2o.vue'; 3 | 4 | export default defineInterface({ 5 | id: 'inline-form-m2o', 6 | name: 'Inline Form', 7 | description: 'Edit a related item inside the current form.', 8 | icon: 'view_agenda', 9 | component: InterfaceInlineFormM2O, 10 | types: ['uuid', 'string', 'text', 'integer', 'bigInteger'], 11 | relational: true, 12 | localTypes: ['m2o'], 13 | group: 'relational', 14 | options: () => { 15 | return [ 16 | { 17 | field: 'createRelatedItem', 18 | type: 'string', 19 | name: 'Create Related Item', 20 | schema: { 21 | default_value: 'withContent', 22 | }, 23 | meta: { 24 | width: 'full', 25 | interface: 'select-dropdown', 26 | options: { 27 | choices: [ 28 | { 29 | text: 'Only with Content', 30 | value: 'withContent', 31 | }, 32 | { 33 | text: 'Always', 34 | value: 'always', 35 | }, 36 | ], 37 | }, 38 | }, 39 | }, 40 | ]; 41 | }, 42 | recommendedDisplays: ['related-values'], 43 | }); 44 | -------------------------------------------------------------------------------- /src/inline-form-m2o.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 169 | 170 | 177 | -------------------------------------------------------------------------------- /src/lang/en.yaml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | inline-form-m2o: 3 | no-read-permission: You do not have permission to read this record. 4 | no-update-permission: You do not have permission to update this record. 5 | -------------------------------------------------------------------------------- /src/shims.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import { DefineComponent } from 'vue'; 3 | const component: DefineComponent<{}, {}, any>; 4 | export default component; 5 | } 6 | -------------------------------------------------------------------------------- /src/types/error.ts: -------------------------------------------------------------------------------- 1 | export type APIError = { 2 | message: string; 3 | extensions: { 4 | code: string; 5 | [key: string]: any; 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './error'; 2 | -------------------------------------------------------------------------------- /src/utils/is-allowed.ts: -------------------------------------------------------------------------------- 1 | import { useStores } from '@directus/extensions-sdk'; 2 | import { FieldFilter, Permission } from '@directus/types'; 3 | import { generateJoi } from '@directus/utils'; 4 | 5 | export function isAllowed( 6 | collection: string, 7 | action: Permission['action'], 8 | value: Record | null, 9 | strict = false 10 | ): boolean { 11 | const { usePermissionsStore, useUserStore } = useStores(); 12 | const permissionsStore = usePermissionsStore(); 13 | const userStore = useUserStore(); 14 | 15 | if (userStore.isAdmin === true) return true; 16 | 17 | const permissions = permissionsStore.permissions; 18 | 19 | const permissionInfo = permissions.find( 20 | (permission) => permission.action === action && permission.collection === collection 21 | ); 22 | 23 | if (!permissionInfo) return false; 24 | if (!permissionInfo.fields && action !== 'share') return false; 25 | 26 | if (strict && action !== 'share' && permissionInfo.fields!.includes('*') === false && value) { 27 | const allowedFields = permissionInfo.fields; 28 | const attemptedFields = Object.keys(value); 29 | 30 | if (attemptedFields.every((field) => allowedFields!.includes(field)) === false) return false; 31 | } 32 | 33 | if (!permissionInfo.permissions || Object.keys(permissionInfo.permissions).length === 0) return true; 34 | 35 | const schema = generateJoi(permissionInfo.permissions as FieldFilter); 36 | 37 | const { error } = schema.validate(value); 38 | 39 | if (!error) { 40 | return true; 41 | } 42 | 43 | return false; 44 | } 45 | -------------------------------------------------------------------------------- /src/utils/unexpected-error.ts: -------------------------------------------------------------------------------- 1 | import { APIError } from '@/types'; 2 | import type { useI18n } from 'vue-i18n'; 3 | 4 | export function unexpectedError(error: Error | any | APIError, store: any, t: ReturnType['t']): void { 5 | const code = 6 | (error as any).response?.data?.errors?.[0]?.extensions?.code || (error as APIError)?.extensions?.code || 'UNKNOWN'; 7 | 8 | // eslint-disable-next-line no-console 9 | console.warn(error); 10 | 11 | store.add({ 12 | title: t(`errors.${code}`), 13 | type: 'error', 14 | code, 15 | dialog: true, 16 | error, 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2019", 4 | "lib": [ 5 | "ES2019", 6 | "DOM" 7 | ], 8 | "moduleResolution": "node", 9 | "strict": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "esModuleInterop": true, 12 | "noImplicitAny": true, 13 | "noImplicitThis": true, 14 | "noImplicitReturns": true, 15 | "noUnusedLocals": true, 16 | "noUncheckedIndexedAccess": true, 17 | "noUnusedParameters": true, 18 | "alwaysStrict": true, 19 | "strictNullChecks": true, 20 | "strictFunctionTypes": true, 21 | "strictBindCallApply": true, 22 | "strictPropertyInitialization": true, 23 | "resolveJsonModule": false, 24 | "skipLibCheck": true, 25 | "forceConsistentCasingInFileNames": true, 26 | "allowSyntheticDefaultImports": true, 27 | "isolatedModules": true, 28 | "rootDir": "./src", 29 | "paths": { 30 | "@/*": [ 31 | "src/*" 32 | ] 33 | } 34 | }, 35 | "include": [ 36 | "./src/**/*.ts" 37 | ] 38 | } 39 | --------------------------------------------------------------------------------