├── .github
└── dependabot.yml
├── .gitignore
├── .scrutinizer.yml
├── LICENSE
├── README.md
├── appinfo
├── app.php
├── info.xml
└── routes.php
├── composer.json
├── composer.lock
├── js
└── gdrive.js
├── l10n
├── .tx
│ └── config
├── af_ZA.js
├── af_ZA.json
├── ar.js
├── ar.json
├── ast.js
├── ast.json
├── bg_BG.js
├── bg_BG.json
├── ca.js
├── ca.json
├── cs_CZ.js
├── cs_CZ.json
├── da.js
├── da.json
├── de.js
├── de.json
├── de_AT.js
├── de_AT.json
├── de_CH.js
├── de_CH.json
├── de_DE.js
├── de_DE.json
├── el.js
├── el.json
├── en_GB.js
├── en_GB.json
├── es.js
├── es.json
├── es_AR.js
├── es_AR.json
├── es_MX.js
├── es_MX.json
├── eu.js
├── eu.json
├── fa.js
├── fa.json
├── fi_FI.js
├── fi_FI.json
├── fr.js
├── fr.json
├── gl.js
├── gl.json
├── he.js
├── he.json
├── hu_HU.js
├── hu_HU.json
├── id.js
├── id.json
├── is.js
├── is.json
├── it.js
├── it.json
├── ja.js
├── ja.json
├── ko.js
├── ko.json
├── lt_LT.js
├── lt_LT.json
├── lv.js
├── lv.json
├── mk.js
├── mk.json
├── nb_NO.js
├── nb_NO.json
├── nl.js
├── nl.json
├── nn_NO.js
├── nn_NO.json
├── oc.js
├── oc.json
├── pl.js
├── pl.json
├── pt_BR.js
├── pt_BR.json
├── pt_PT.js
├── pt_PT.json
├── ru.js
├── ru.json
├── ru_RU.js
├── ru_RU.json
├── sl.js
├── sl.json
├── sq.js
├── sq.json
├── sv.js
├── sv.json
├── th_TH.js
├── th_TH.json
├── tr.js
├── tr.json
├── ug.js
├── ug.json
├── uk.js
├── uk.json
├── zh_CN.js
├── zh_CN.json
├── zh_TW.js
└── zh_TW.json
├── lib
├── AppInfo
│ └── Application.php
├── Backend
│ └── Google.php
├── Controller
│ └── OauthController.php
└── Storage
│ └── Google.php
└── tests
├── GoogleTest.php
├── bootstrap.php
├── config.json.example
└── phpunit.xml
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: composer
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | time: "22:00"
8 | open-pull-requests-limit: 10
9 | ignore:
10 | - dependency-name: guzzlehttp/guzzle
11 | versions:
12 | - "< 7, >= 6.0.a"
13 | - dependency-name: guzzlehttp/guzzle
14 | versions:
15 | - 7.2.0
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | tests/config.json
2 | tests/clover.xml
3 | vendor
4 |
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | filter:
2 | excluded_paths:
3 | - 'tests/*'
4 |
5 | imports:
6 | - php
7 |
8 | tools:
9 | external_code_coverage: true
10 |
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU AFFERO GENERAL PUBLIC LICENSE
2 | Version 3, 19 November 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU Affero General Public License is a free, copyleft license for
11 | software and other kinds of works, specifically designed to ensure
12 | cooperation with the community in the case of network server software.
13 |
14 | The licenses for most software and other practical works are designed
15 | to take away your freedom to share and change the works. By contrast,
16 | our General Public Licenses are intended to guarantee your freedom to
17 | share and change all versions of a program--to make sure it remains free
18 | software for all its users.
19 |
20 | When we speak of free software, we are referring to freedom, not
21 | price. Our General Public Licenses are designed to make sure that you
22 | have the freedom to distribute copies of free software (and charge for
23 | them if you wish), that you receive source code or can get it if you
24 | want it, that you can change the software or use pieces of it in new
25 | free programs, and that you know you can do these things.
26 |
27 | Developers that use our General Public Licenses protect your rights
28 | with two steps: (1) assert copyright on the software, and (2) offer
29 | you this License which gives you legal permission to copy, distribute
30 | and/or modify the software.
31 |
32 | A secondary benefit of defending all users' freedom is that
33 | improvements made in alternate versions of the program, if they
34 | receive widespread use, become available for other developers to
35 | incorporate. Many developers of free software are heartened and
36 | encouraged by the resulting cooperation. However, in the case of
37 | software used on network servers, this result may fail to come about.
38 | The GNU General Public License permits making a modified version and
39 | letting the public access it on a server without ever releasing its
40 | source code to the public.
41 |
42 | The GNU Affero General Public License is designed specifically to
43 | ensure that, in such cases, the modified source code becomes available
44 | to the community. It requires the operator of a network server to
45 | provide the source code of the modified version running there to the
46 | users of that server. Therefore, public use of a modified version, on
47 | a publicly accessible server, gives the public access to the source
48 | code of the modified version.
49 |
50 | An older license, called the Affero General Public License and
51 | published by Affero, was designed to accomplish similar goals. This is
52 | a different license, not a version of the Affero GPL, but Affero has
53 | released a new version of the Affero GPL which permits relicensing under
54 | this license.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | TERMS AND CONDITIONS
60 |
61 | 0. Definitions.
62 |
63 | "This License" refers to version 3 of the GNU Affero General Public License.
64 |
65 | "Copyright" also means copyright-like laws that apply to other kinds of
66 | works, such as semiconductor masks.
67 |
68 | "The Program" refers to any copyrightable work licensed under this
69 | License. Each licensee is addressed as "you". "Licensees" and
70 | "recipients" may be individuals or organizations.
71 |
72 | To "modify" a work means to copy from or adapt all or part of the work
73 | in a fashion requiring copyright permission, other than the making of an
74 | exact copy. The resulting work is called a "modified version" of the
75 | earlier work or a work "based on" the earlier work.
76 |
77 | A "covered work" means either the unmodified Program or a work based
78 | on the Program.
79 |
80 | To "propagate" a work means to do anything with it that, without
81 | permission, would make you directly or secondarily liable for
82 | infringement under applicable copyright law, except executing it on a
83 | computer or modifying a private copy. Propagation includes copying,
84 | distribution (with or without modification), making available to the
85 | public, and in some countries other activities as well.
86 |
87 | To "convey" a work means any kind of propagation that enables other
88 | parties to make or receive copies. Mere interaction with a user through
89 | a computer network, with no transfer of a copy, is not conveying.
90 |
91 | An interactive user interface displays "Appropriate Legal Notices"
92 | to the extent that it includes a convenient and prominently visible
93 | feature that (1) displays an appropriate copyright notice, and (2)
94 | tells the user that there is no warranty for the work (except to the
95 | extent that warranties are provided), that licensees may convey the
96 | work under this License, and how to view a copy of this License. If
97 | the interface presents a list of user commands or options, such as a
98 | menu, a prominent item in the list meets this criterion.
99 |
100 | 1. Source Code.
101 |
102 | The "source code" for a work means the preferred form of the work
103 | for making modifications to it. "Object code" means any non-source
104 | form of a work.
105 |
106 | A "Standard Interface" means an interface that either is an official
107 | standard defined by a recognized standards body, or, in the case of
108 | interfaces specified for a particular programming language, one that
109 | is widely used among developers working in that language.
110 |
111 | The "System Libraries" of an executable work include anything, other
112 | than the work as a whole, that (a) is included in the normal form of
113 | packaging a Major Component, but which is not part of that Major
114 | Component, and (b) serves only to enable use of the work with that
115 | Major Component, or to implement a Standard Interface for which an
116 | implementation is available to the public in source code form. A
117 | "Major Component", in this context, means a major essential component
118 | (kernel, window system, and so on) of the specific operating system
119 | (if any) on which the executable work runs, or a compiler used to
120 | produce the work, or an object code interpreter used to run it.
121 |
122 | The "Corresponding Source" for a work in object code form means all
123 | the source code needed to generate, install, and (for an executable
124 | work) run the object code and to modify the work, including scripts to
125 | control those activities. However, it does not include the work's
126 | System Libraries, or general-purpose tools or generally available free
127 | programs which are used unmodified in performing those activities but
128 | which are not part of the work. For example, Corresponding Source
129 | includes interface definition files associated with source files for
130 | the work, and the source code for shared libraries and dynamically
131 | linked subprograms that the work is specifically designed to require,
132 | such as by intimate data communication or control flow between those
133 | subprograms and other parts of the work.
134 |
135 | The Corresponding Source need not include anything that users
136 | can regenerate automatically from other parts of the Corresponding
137 | Source.
138 |
139 | The Corresponding Source for a work in source code form is that
140 | same work.
141 |
142 | 2. Basic Permissions.
143 |
144 | All rights granted under this License are granted for the term of
145 | copyright on the Program, and are irrevocable provided the stated
146 | conditions are met. This License explicitly affirms your unlimited
147 | permission to run the unmodified Program. The output from running a
148 | covered work is covered by this License only if the output, given its
149 | content, constitutes a covered work. This License acknowledges your
150 | rights of fair use or other equivalent, as provided by copyright law.
151 |
152 | You may make, run and propagate covered works that you do not
153 | convey, without conditions so long as your license otherwise remains
154 | in force. You may convey covered works to others for the sole purpose
155 | of having them make modifications exclusively for you, or provide you
156 | with facilities for running those works, provided that you comply with
157 | the terms of this License in conveying all material for which you do
158 | not control copyright. Those thus making or running the covered works
159 | for you must do so exclusively on your behalf, under your direction
160 | and control, on terms that prohibit them from making any copies of
161 | your copyrighted material outside their relationship with you.
162 |
163 | Conveying under any other circumstances is permitted solely under
164 | the conditions stated below. Sublicensing is not allowed; section 10
165 | makes it unnecessary.
166 |
167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
168 |
169 | No covered work shall be deemed part of an effective technological
170 | measure under any applicable law fulfilling obligations under article
171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
172 | similar laws prohibiting or restricting circumvention of such
173 | measures.
174 |
175 | When you convey a covered work, you waive any legal power to forbid
176 | circumvention of technological measures to the extent such circumvention
177 | is effected by exercising rights under this License with respect to
178 | the covered work, and you disclaim any intention to limit operation or
179 | modification of the work as a means of enforcing, against the work's
180 | users, your or third parties' legal rights to forbid circumvention of
181 | technological measures.
182 |
183 | 4. Conveying Verbatim Copies.
184 |
185 | You may convey verbatim copies of the Program's source code as you
186 | receive it, in any medium, provided that you conspicuously and
187 | appropriately publish on each copy an appropriate copyright notice;
188 | keep intact all notices stating that this License and any
189 | non-permissive terms added in accord with section 7 apply to the code;
190 | keep intact all notices of the absence of any warranty; and give all
191 | recipients a copy of this License along with the Program.
192 |
193 | You may charge any price or no price for each copy that you convey,
194 | and you may offer support or warranty protection for a fee.
195 |
196 | 5. Conveying Modified Source Versions.
197 |
198 | You may convey a work based on the Program, or the modifications to
199 | produce it from the Program, in the form of source code under the
200 | terms of section 4, provided that you also meet all of these conditions:
201 |
202 | a) The work must carry prominent notices stating that you modified
203 | it, and giving a relevant date.
204 |
205 | b) The work must carry prominent notices stating that it is
206 | released under this License and any conditions added under section
207 | 7. This requirement modifies the requirement in section 4 to
208 | "keep intact all notices".
209 |
210 | c) You must license the entire work, as a whole, under this
211 | License to anyone who comes into possession of a copy. This
212 | License will therefore apply, along with any applicable section 7
213 | additional terms, to the whole of the work, and all its parts,
214 | regardless of how they are packaged. This License gives no
215 | permission to license the work in any other way, but it does not
216 | invalidate such permission if you have separately received it.
217 |
218 | d) If the work has interactive user interfaces, each must display
219 | Appropriate Legal Notices; however, if the Program has interactive
220 | interfaces that do not display Appropriate Legal Notices, your
221 | work need not make them do so.
222 |
223 | A compilation of a covered work with other separate and independent
224 | works, which are not by their nature extensions of the covered work,
225 | and which are not combined with it such as to form a larger program,
226 | in or on a volume of a storage or distribution medium, is called an
227 | "aggregate" if the compilation and its resulting copyright are not
228 | used to limit the access or legal rights of the compilation's users
229 | beyond what the individual works permit. Inclusion of a covered work
230 | in an aggregate does not cause this License to apply to the other
231 | parts of the aggregate.
232 |
233 | 6. Conveying Non-Source Forms.
234 |
235 | You may convey a covered work in object code form under the terms
236 | of sections 4 and 5, provided that you also convey the
237 | machine-readable Corresponding Source under the terms of this License,
238 | in one of these ways:
239 |
240 | a) Convey the object code in, or embodied in, a physical product
241 | (including a physical distribution medium), accompanied by the
242 | Corresponding Source fixed on a durable physical medium
243 | customarily used for software interchange.
244 |
245 | b) Convey the object code in, or embodied in, a physical product
246 | (including a physical distribution medium), accompanied by a
247 | written offer, valid for at least three years and valid for as
248 | long as you offer spare parts or customer support for that product
249 | model, to give anyone who possesses the object code either (1) a
250 | copy of the Corresponding Source for all the software in the
251 | product that is covered by this License, on a durable physical
252 | medium customarily used for software interchange, for a price no
253 | more than your reasonable cost of physically performing this
254 | conveying of source, or (2) access to copy the
255 | Corresponding Source from a network server at no charge.
256 |
257 | c) Convey individual copies of the object code with a copy of the
258 | written offer to provide the Corresponding Source. This
259 | alternative is allowed only occasionally and noncommercially, and
260 | only if you received the object code with such an offer, in accord
261 | with subsection 6b.
262 |
263 | d) Convey the object code by offering access from a designated
264 | place (gratis or for a charge), and offer equivalent access to the
265 | Corresponding Source in the same way through the same place at no
266 | further charge. You need not require recipients to copy the
267 | Corresponding Source along with the object code. If the place to
268 | copy the object code is a network server, the Corresponding Source
269 | may be on a different server (operated by you or a third party)
270 | that supports equivalent copying facilities, provided you maintain
271 | clear directions next to the object code saying where to find the
272 | Corresponding Source. Regardless of what server hosts the
273 | Corresponding Source, you remain obligated to ensure that it is
274 | available for as long as needed to satisfy these requirements.
275 |
276 | e) Convey the object code using peer-to-peer transmission, provided
277 | you inform other peers where the object code and Corresponding
278 | Source of the work are being offered to the general public at no
279 | charge under subsection 6d.
280 |
281 | A separable portion of the object code, whose source code is excluded
282 | from the Corresponding Source as a System Library, need not be
283 | included in conveying the object code work.
284 |
285 | A "User Product" is either (1) a "consumer product", which means any
286 | tangible personal property which is normally used for personal, family,
287 | or household purposes, or (2) anything designed or sold for incorporation
288 | into a dwelling. In determining whether a product is a consumer product,
289 | doubtful cases shall be resolved in favor of coverage. For a particular
290 | product received by a particular user, "normally used" refers to a
291 | typical or common use of that class of product, regardless of the status
292 | of the particular user or of the way in which the particular user
293 | actually uses, or expects or is expected to use, the product. A product
294 | is a consumer product regardless of whether the product has substantial
295 | commercial, industrial or non-consumer uses, unless such uses represent
296 | the only significant mode of use of the product.
297 |
298 | "Installation Information" for a User Product means any methods,
299 | procedures, authorization keys, or other information required to install
300 | and execute modified versions of a covered work in that User Product from
301 | a modified version of its Corresponding Source. The information must
302 | suffice to ensure that the continued functioning of the modified object
303 | code is in no case prevented or interfered with solely because
304 | modification has been made.
305 |
306 | If you convey an object code work under this section in, or with, or
307 | specifically for use in, a User Product, and the conveying occurs as
308 | part of a transaction in which the right of possession and use of the
309 | User Product is transferred to the recipient in perpetuity or for a
310 | fixed term (regardless of how the transaction is characterized), the
311 | Corresponding Source conveyed under this section must be accompanied
312 | by the Installation Information. But this requirement does not apply
313 | if neither you nor any third party retains the ability to install
314 | modified object code on the User Product (for example, the work has
315 | been installed in ROM).
316 |
317 | The requirement to provide Installation Information does not include a
318 | requirement to continue to provide support service, warranty, or updates
319 | for a work that has been modified or installed by the recipient, or for
320 | the User Product in which it has been modified or installed. Access to a
321 | network may be denied when the modification itself materially and
322 | adversely affects the operation of the network or violates the rules and
323 | protocols for communication across the network.
324 |
325 | Corresponding Source conveyed, and Installation Information provided,
326 | in accord with this section must be in a format that is publicly
327 | documented (and with an implementation available to the public in
328 | source code form), and must require no special password or key for
329 | unpacking, reading or copying.
330 |
331 | 7. Additional Terms.
332 |
333 | "Additional permissions" are terms that supplement the terms of this
334 | License by making exceptions from one or more of its conditions.
335 | Additional permissions that are applicable to the entire Program shall
336 | be treated as though they were included in this License, to the extent
337 | that they are valid under applicable law. If additional permissions
338 | apply only to part of the Program, that part may be used separately
339 | under those permissions, but the entire Program remains governed by
340 | this License without regard to the additional permissions.
341 |
342 | When you convey a copy of a covered work, you may at your option
343 | remove any additional permissions from that copy, or from any part of
344 | it. (Additional permissions may be written to require their own
345 | removal in certain cases when you modify the work.) You may place
346 | additional permissions on material, added by you to a covered work,
347 | for which you have or can give appropriate copyright permission.
348 |
349 | Notwithstanding any other provision of this License, for material you
350 | add to a covered work, you may (if authorized by the copyright holders of
351 | that material) supplement the terms of this License with terms:
352 |
353 | a) Disclaiming warranty or limiting liability differently from the
354 | terms of sections 15 and 16 of this License; or
355 |
356 | b) Requiring preservation of specified reasonable legal notices or
357 | author attributions in that material or in the Appropriate Legal
358 | Notices displayed by works containing it; or
359 |
360 | c) Prohibiting misrepresentation of the origin of that material, or
361 | requiring that modified versions of such material be marked in
362 | reasonable ways as different from the original version; or
363 |
364 | d) Limiting the use for publicity purposes of names of licensors or
365 | authors of the material; or
366 |
367 | e) Declining to grant rights under trademark law for use of some
368 | trade names, trademarks, or service marks; or
369 |
370 | f) Requiring indemnification of licensors and authors of that
371 | material by anyone who conveys the material (or modified versions of
372 | it) with contractual assumptions of liability to the recipient, for
373 | any liability that these contractual assumptions directly impose on
374 | those licensors and authors.
375 |
376 | All other non-permissive additional terms are considered "further
377 | restrictions" within the meaning of section 10. If the Program as you
378 | received it, or any part of it, contains a notice stating that it is
379 | governed by this License along with a term that is a further
380 | restriction, you may remove that term. If a license document contains
381 | a further restriction but permits relicensing or conveying under this
382 | License, you may add to a covered work material governed by the terms
383 | of that license document, provided that the further restriction does
384 | not survive such relicensing or conveying.
385 |
386 | If you add terms to a covered work in accord with this section, you
387 | must place, in the relevant source files, a statement of the
388 | additional terms that apply to those files, or a notice indicating
389 | where to find the applicable terms.
390 |
391 | Additional terms, permissive or non-permissive, may be stated in the
392 | form of a separately written license, or stated as exceptions;
393 | the above requirements apply either way.
394 |
395 | 8. Termination.
396 |
397 | You may not propagate or modify a covered work except as expressly
398 | provided under this License. Any attempt otherwise to propagate or
399 | modify it is void, and will automatically terminate your rights under
400 | this License (including any patent licenses granted under the third
401 | paragraph of section 11).
402 |
403 | However, if you cease all violation of this License, then your
404 | license from a particular copyright holder is reinstated (a)
405 | provisionally, unless and until the copyright holder explicitly and
406 | finally terminates your license, and (b) permanently, if the copyright
407 | holder fails to notify you of the violation by some reasonable means
408 | prior to 60 days after the cessation.
409 |
410 | Moreover, your license from a particular copyright holder is
411 | reinstated permanently if the copyright holder notifies you of the
412 | violation by some reasonable means, this is the first time you have
413 | received notice of violation of this License (for any work) from that
414 | copyright holder, and you cure the violation prior to 30 days after
415 | your receipt of the notice.
416 |
417 | Termination of your rights under this section does not terminate the
418 | licenses of parties who have received copies or rights from you under
419 | this License. If your rights have been terminated and not permanently
420 | reinstated, you do not qualify to receive new licenses for the same
421 | material under section 10.
422 |
423 | 9. Acceptance Not Required for Having Copies.
424 |
425 | You are not required to accept this License in order to receive or
426 | run a copy of the Program. Ancillary propagation of a covered work
427 | occurring solely as a consequence of using peer-to-peer transmission
428 | to receive a copy likewise does not require acceptance. However,
429 | nothing other than this License grants you permission to propagate or
430 | modify any covered work. These actions infringe copyright if you do
431 | not accept this License. Therefore, by modifying or propagating a
432 | covered work, you indicate your acceptance of this License to do so.
433 |
434 | 10. Automatic Licensing of Downstream Recipients.
435 |
436 | Each time you convey a covered work, the recipient automatically
437 | receives a license from the original licensors, to run, modify and
438 | propagate that work, subject to this License. You are not responsible
439 | for enforcing compliance by third parties with this License.
440 |
441 | An "entity transaction" is a transaction transferring control of an
442 | organization, or substantially all assets of one, or subdividing an
443 | organization, or merging organizations. If propagation of a covered
444 | work results from an entity transaction, each party to that
445 | transaction who receives a copy of the work also receives whatever
446 | licenses to the work the party's predecessor in interest had or could
447 | give under the previous paragraph, plus a right to possession of the
448 | Corresponding Source of the work from the predecessor in interest, if
449 | the predecessor has it or can get it with reasonable efforts.
450 |
451 | You may not impose any further restrictions on the exercise of the
452 | rights granted or affirmed under this License. For example, you may
453 | not impose a license fee, royalty, or other charge for exercise of
454 | rights granted under this License, and you may not initiate litigation
455 | (including a cross-claim or counterclaim in a lawsuit) alleging that
456 | any patent claim is infringed by making, using, selling, offering for
457 | sale, or importing the Program or any portion of it.
458 |
459 | 11. Patents.
460 |
461 | A "contributor" is a copyright holder who authorizes use under this
462 | License of the Program or a work on which the Program is based. The
463 | work thus licensed is called the contributor's "contributor version".
464 |
465 | A contributor's "essential patent claims" are all patent claims
466 | owned or controlled by the contributor, whether already acquired or
467 | hereafter acquired, that would be infringed by some manner, permitted
468 | by this License, of making, using, or selling its contributor version,
469 | but do not include claims that would be infringed only as a
470 | consequence of further modification of the contributor version. For
471 | purposes of this definition, "control" includes the right to grant
472 | patent sublicenses in a manner consistent with the requirements of
473 | this License.
474 |
475 | Each contributor grants you a non-exclusive, worldwide, royalty-free
476 | patent license under the contributor's essential patent claims, to
477 | make, use, sell, offer for sale, import and otherwise run, modify and
478 | propagate the contents of its contributor version.
479 |
480 | In the following three paragraphs, a "patent license" is any express
481 | agreement or commitment, however denominated, not to enforce a patent
482 | (such as an express permission to practice a patent or covenant not to
483 | sue for patent infringement). To "grant" such a patent license to a
484 | party means to make such an agreement or commitment not to enforce a
485 | patent against the party.
486 |
487 | If you convey a covered work, knowingly relying on a patent license,
488 | and the Corresponding Source of the work is not available for anyone
489 | to copy, free of charge and under the terms of this License, through a
490 | publicly available network server or other readily accessible means,
491 | then you must either (1) cause the Corresponding Source to be so
492 | available, or (2) arrange to deprive yourself of the benefit of the
493 | patent license for this particular work, or (3) arrange, in a manner
494 | consistent with the requirements of this License, to extend the patent
495 | license to downstream recipients. "Knowingly relying" means you have
496 | actual knowledge that, but for the patent license, your conveying the
497 | covered work in a country, or your recipient's use of the covered work
498 | in a country, would infringe one or more identifiable patents in that
499 | country that you have reason to believe are valid.
500 |
501 | If, pursuant to or in connection with a single transaction or
502 | arrangement, you convey, or propagate by procuring conveyance of, a
503 | covered work, and grant a patent license to some of the parties
504 | receiving the covered work authorizing them to use, propagate, modify
505 | or convey a specific copy of the covered work, then the patent license
506 | you grant is automatically extended to all recipients of the covered
507 | work and works based on it.
508 |
509 | A patent license is "discriminatory" if it does not include within
510 | the scope of its coverage, prohibits the exercise of, or is
511 | conditioned on the non-exercise of one or more of the rights that are
512 | specifically granted under this License. You may not convey a covered
513 | work if you are a party to an arrangement with a third party that is
514 | in the business of distributing software, under which you make payment
515 | to the third party based on the extent of your activity of conveying
516 | the work, and under which the third party grants, to any of the
517 | parties who would receive the covered work from you, a discriminatory
518 | patent license (a) in connection with copies of the covered work
519 | conveyed by you (or copies made from those copies), or (b) primarily
520 | for and in connection with specific products or compilations that
521 | contain the covered work, unless you entered into that arrangement,
522 | or that patent license was granted, prior to 28 March 2007.
523 |
524 | Nothing in this License shall be construed as excluding or limiting
525 | any implied license or other defenses to infringement that may
526 | otherwise be available to you under applicable patent law.
527 |
528 | 12. No Surrender of Others' Freedom.
529 |
530 | If conditions are imposed on you (whether by court order, agreement or
531 | otherwise) that contradict the conditions of this License, they do not
532 | excuse you from the conditions of this License. If you cannot convey a
533 | covered work so as to satisfy simultaneously your obligations under this
534 | License and any other pertinent obligations, then as a consequence you may
535 | not convey it at all. For example, if you agree to terms that obligate you
536 | to collect a royalty for further conveying from those to whom you convey
537 | the Program, the only way you could satisfy both those terms and this
538 | License would be to refrain entirely from conveying the Program.
539 |
540 | 13. Remote Network Interaction; Use with the GNU General Public License.
541 |
542 | Notwithstanding any other provision of this License, if you modify the
543 | Program, your modified version must prominently offer all users
544 | interacting with it remotely through a computer network (if your version
545 | supports such interaction) an opportunity to receive the Corresponding
546 | Source of your version by providing access to the Corresponding Source
547 | from a network server at no charge, through some standard or customary
548 | means of facilitating copying of software. This Corresponding Source
549 | shall include the Corresponding Source for any work covered by version 3
550 | of the GNU General Public License that is incorporated pursuant to the
551 | following paragraph.
552 |
553 | Notwithstanding any other provision of this License, you have
554 | permission to link or combine any covered work with a work licensed
555 | under version 3 of the GNU General Public License into a single
556 | combined work, and to convey the resulting work. The terms of this
557 | License will continue to apply to the part which is the covered work,
558 | but the work with which it is combined will remain governed by version
559 | 3 of the GNU General Public License.
560 |
561 | 14. Revised Versions of this License.
562 |
563 | The Free Software Foundation may publish revised and/or new versions of
564 | the GNU Affero General Public License from time to time. Such new versions
565 | will be similar in spirit to the present version, but may differ in detail to
566 | address new problems or concerns.
567 |
568 | Each version is given a distinguishing version number. If the
569 | Program specifies that a certain numbered version of the GNU Affero General
570 | Public License "or any later version" applies to it, you have the
571 | option of following the terms and conditions either of that numbered
572 | version or of any later version published by the Free Software
573 | Foundation. If the Program does not specify a version number of the
574 | GNU Affero General Public License, you may choose any version ever published
575 | by the Free Software Foundation.
576 |
577 | If the Program specifies that a proxy can decide which future
578 | versions of the GNU Affero General Public License can be used, that proxy's
579 | public statement of acceptance of a version permanently authorizes you
580 | to choose that version for the Program.
581 |
582 | Later license versions may give you additional or different
583 | permissions. However, no additional obligations are imposed on any
584 | author or copyright holder as a result of your choosing to follow a
585 | later version.
586 |
587 | 15. Disclaimer of Warranty.
588 |
589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
597 |
598 | 16. Limitation of Liability.
599 |
600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
608 | SUCH DAMAGES.
609 |
610 | 17. Interpretation of Sections 15 and 16.
611 |
612 | If the disclaimer of warranty and limitation of liability provided
613 | above cannot be given local legal effect according to their terms,
614 | reviewing courts shall apply local law that most closely approximates
615 | an absolute waiver of all civil liability in connection with the
616 | Program, unless a warranty or assumption of liability accompanies a
617 | copy of the Program in return for a fee.
618 |
619 | END OF TERMS AND CONDITIONS
620 |
621 | How to Apply These Terms to Your New Programs
622 |
623 | If you develop a new program, and you want it to be of the greatest
624 | possible use to the public, the best way to achieve this is to make it
625 | free software which everyone can redistribute and change under these terms.
626 |
627 | To do so, attach the following notices to the program. It is safest
628 | to attach them to the start of each source file to most effectively
629 | state the exclusion of warranty; and each file should have at least
630 | the "copyright" line and a pointer to where the full notice is found.
631 |
632 |
633 | Copyright (C)
634 |
635 | This program is free software: you can redistribute it and/or modify
636 | it under the terms of the GNU Affero General Public License as published
637 | by the Free Software Foundation, either version 3 of the License, or
638 | (at your option) any later version.
639 |
640 | This program is distributed in the hope that it will be useful,
641 | but WITHOUT ANY WARRANTY; without even the implied warranty of
642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643 | GNU Affero General Public License for more details.
644 |
645 | You should have received a copy of the GNU Affero General Public License
646 | along with this program. If not, see .
647 |
648 | Also add information on how to contact you by electronic and paper mail.
649 |
650 | If your software can interact with users remotely through a computer
651 | network, you should also make sure that it provides a way for users to
652 | get its source. For example, if your program is a web application, its
653 | interface could display a "Source" link that leads users to an archive
654 | of the code. There are many ways you could offer source, and different
655 | solutions will be better for different programs; see section 13 for the
656 | specific requirements.
657 |
658 | You should also get your employer (if you work as a programmer) or school,
659 | if any, to sign a "copyright disclaimer" for the program, if necessary.
660 | For more information on this, and how to apply and follow the GNU AGPL, see
661 | .
662 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # files_external_gdrive
2 | Google Drive backend for ownCloud
3 |
4 | Requires ownCloud 10.0 or later
5 |
6 | [](https://travis-ci.org/owncloud/files_external_gdrive)
7 | [](https://scrutinizer-ci.com/g/owncloud/files_external_gdrive/?branch=master)
8 | [](https://scrutinizer-ci.com/g/owncloud/files_external_gdrive/?branch=master)
9 |
--------------------------------------------------------------------------------
/appinfo/app.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | files_external_gdrive
4 | Google Drive external storage support
5 | GDrive backend of external storage
6 | AGPL
7 | Vincent Petry
8 | 0.1.0
9 |
10 |
11 |
12 |
13 | Files_external_gdrive
14 |
15 |
16 |
17 | https://github.com/owncloud/files_external_gdrive/
18 | https://github.com/owncloud/files_external_gdrive/issues
19 | https://github.com/owncloud/files_external_gdrive.git
20 |
21 |
--------------------------------------------------------------------------------
/appinfo/routes.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * @copyright Copyright (c) 2017, ownCloud GmbH
6 | * @license AGPL-3.0
7 | *
8 | * This code is free software: you can redistribute it and/or modify
9 | * it under the terms of the GNU Affero General Public License, version 3,
10 | * as published by the Free Software Foundation.
11 | *
12 | * This program is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License, version 3,
18 | * along with this program. If not, see
19 | *
20 | */
21 |
22 | $application = new \OCA\Files_external_gdrive\AppInfo\Application();
23 | $application->registerRoutes($this, [
24 | 'routes' => [
25 | ['name' => 'oauth#receiveToken', 'url' => '/oauth', 'verb' => 'POST'],
26 | ],
27 | ]);
28 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "google/apiclient": "1.1.7",
4 | "guzzlehttp/guzzle": "^7.4"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "8b2bb078d813ca7e7c700250e5916ace",
8 | "packages": [
9 | {
10 | "name": "google/apiclient",
11 | "version": "1.1.7",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/googleapis/google-api-php-client.git",
15 | "reference": "400f250a30ae1dd4c4a0a4f750fe973fc70e6311"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/googleapis/google-api-php-client/zipball/400f250a30ae1dd4c4a0a4f750fe973fc70e6311",
20 | "reference": "400f250a30ae1dd4c4a0a4f750fe973fc70e6311",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "php": ">=5.2.1"
25 | },
26 | "require-dev": {
27 | "phpunit/phpunit": "3.7.*",
28 | "squizlabs/php_codesniffer": "~2.3"
29 | },
30 | "type": "library",
31 | "extra": {
32 | "branch-alias": {
33 | "dev-master": "1.1.x-dev"
34 | }
35 | },
36 | "autoload": {
37 | "classmap": [
38 | "src/"
39 | ]
40 | },
41 | "notification-url": "https://packagist.org/downloads/",
42 | "license": [
43 | "Apache-2.0"
44 | ],
45 | "description": "Client library for Google APIs",
46 | "homepage": "http://developers.google.com/api-client-library/php",
47 | "keywords": [
48 | "google"
49 | ],
50 | "time": "2016-02-02T18:50:42+00:00"
51 | },
52 | {
53 | "name": "guzzlehttp/guzzle",
54 | "version": "7.5.0",
55 | "source": {
56 | "type": "git",
57 | "url": "https://github.com/guzzle/guzzle.git",
58 | "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba"
59 | },
60 | "dist": {
61 | "type": "zip",
62 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba",
63 | "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba",
64 | "shasum": ""
65 | },
66 | "require": {
67 | "ext-json": "*",
68 | "guzzlehttp/promises": "^1.5",
69 | "guzzlehttp/psr7": "^1.9 || ^2.4",
70 | "php": "^7.2.5 || ^8.0",
71 | "psr/http-client": "^1.0",
72 | "symfony/deprecation-contracts": "^2.2 || ^3.0"
73 | },
74 | "provide": {
75 | "psr/http-client-implementation": "1.0"
76 | },
77 | "require-dev": {
78 | "bamarni/composer-bin-plugin": "^1.8.1",
79 | "ext-curl": "*",
80 | "php-http/client-integration-tests": "^3.0",
81 | "phpunit/phpunit": "^8.5.29 || ^9.5.23",
82 | "psr/log": "^1.1 || ^2.0 || ^3.0"
83 | },
84 | "suggest": {
85 | "ext-curl": "Required for CURL handler support",
86 | "ext-intl": "Required for Internationalized Domain Name (IDN) support",
87 | "psr/log": "Required for using the Log middleware"
88 | },
89 | "type": "library",
90 | "extra": {
91 | "bamarni-bin": {
92 | "bin-links": true,
93 | "forward-command": false
94 | },
95 | "branch-alias": {
96 | "dev-master": "7.5-dev"
97 | }
98 | },
99 | "autoload": {
100 | "files": [
101 | "src/functions_include.php"
102 | ],
103 | "psr-4": {
104 | "GuzzleHttp\\": "src/"
105 | }
106 | },
107 | "notification-url": "https://packagist.org/downloads/",
108 | "license": [
109 | "MIT"
110 | ],
111 | "authors": [
112 | {
113 | "name": "Graham Campbell",
114 | "email": "hello@gjcampbell.co.uk",
115 | "homepage": "https://github.com/GrahamCampbell"
116 | },
117 | {
118 | "name": "Michael Dowling",
119 | "email": "mtdowling@gmail.com",
120 | "homepage": "https://github.com/mtdowling"
121 | },
122 | {
123 | "name": "Jeremy Lindblom",
124 | "email": "jeremeamia@gmail.com",
125 | "homepage": "https://github.com/jeremeamia"
126 | },
127 | {
128 | "name": "George Mponos",
129 | "email": "gmponos@gmail.com",
130 | "homepage": "https://github.com/gmponos"
131 | },
132 | {
133 | "name": "Tobias Nyholm",
134 | "email": "tobias.nyholm@gmail.com",
135 | "homepage": "https://github.com/Nyholm"
136 | },
137 | {
138 | "name": "Márk Sági-Kazár",
139 | "email": "mark.sagikazar@gmail.com",
140 | "homepage": "https://github.com/sagikazarmark"
141 | },
142 | {
143 | "name": "Tobias Schultze",
144 | "email": "webmaster@tubo-world.de",
145 | "homepage": "https://github.com/Tobion"
146 | }
147 | ],
148 | "description": "Guzzle is a PHP HTTP client library",
149 | "keywords": [
150 | "client",
151 | "curl",
152 | "framework",
153 | "http",
154 | "http client",
155 | "psr-18",
156 | "psr-7",
157 | "rest",
158 | "web service"
159 | ],
160 | "support": {
161 | "issues": "https://github.com/guzzle/guzzle/issues",
162 | "source": "https://github.com/guzzle/guzzle/tree/7.5.0"
163 | },
164 | "funding": [
165 | {
166 | "url": "https://github.com/GrahamCampbell",
167 | "type": "github"
168 | },
169 | {
170 | "url": "https://github.com/Nyholm",
171 | "type": "github"
172 | },
173 | {
174 | "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
175 | "type": "tidelift"
176 | }
177 | ],
178 | "time": "2022-08-28T15:39:27+00:00"
179 | },
180 | {
181 | "name": "guzzlehttp/promises",
182 | "version": "1.5.2",
183 | "source": {
184 | "type": "git",
185 | "url": "https://github.com/guzzle/promises.git",
186 | "reference": "b94b2807d85443f9719887892882d0329d1e2598"
187 | },
188 | "dist": {
189 | "type": "zip",
190 | "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598",
191 | "reference": "b94b2807d85443f9719887892882d0329d1e2598",
192 | "shasum": ""
193 | },
194 | "require": {
195 | "php": ">=5.5"
196 | },
197 | "require-dev": {
198 | "symfony/phpunit-bridge": "^4.4 || ^5.1"
199 | },
200 | "type": "library",
201 | "extra": {
202 | "branch-alias": {
203 | "dev-master": "1.5-dev"
204 | }
205 | },
206 | "autoload": {
207 | "files": [
208 | "src/functions_include.php"
209 | ],
210 | "psr-4": {
211 | "GuzzleHttp\\Promise\\": "src/"
212 | }
213 | },
214 | "notification-url": "https://packagist.org/downloads/",
215 | "license": [
216 | "MIT"
217 | ],
218 | "authors": [
219 | {
220 | "name": "Graham Campbell",
221 | "email": "hello@gjcampbell.co.uk",
222 | "homepage": "https://github.com/GrahamCampbell"
223 | },
224 | {
225 | "name": "Michael Dowling",
226 | "email": "mtdowling@gmail.com",
227 | "homepage": "https://github.com/mtdowling"
228 | },
229 | {
230 | "name": "Tobias Nyholm",
231 | "email": "tobias.nyholm@gmail.com",
232 | "homepage": "https://github.com/Nyholm"
233 | },
234 | {
235 | "name": "Tobias Schultze",
236 | "email": "webmaster@tubo-world.de",
237 | "homepage": "https://github.com/Tobion"
238 | }
239 | ],
240 | "description": "Guzzle promises library",
241 | "keywords": [
242 | "promise"
243 | ],
244 | "support": {
245 | "issues": "https://github.com/guzzle/promises/issues",
246 | "source": "https://github.com/guzzle/promises/tree/1.5.2"
247 | },
248 | "funding": [
249 | {
250 | "url": "https://github.com/GrahamCampbell",
251 | "type": "github"
252 | },
253 | {
254 | "url": "https://github.com/Nyholm",
255 | "type": "github"
256 | },
257 | {
258 | "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
259 | "type": "tidelift"
260 | }
261 | ],
262 | "time": "2022-08-28T14:55:35+00:00"
263 | },
264 | {
265 | "name": "guzzlehttp/psr7",
266 | "version": "2.4.1",
267 | "source": {
268 | "type": "git",
269 | "url": "https://github.com/guzzle/psr7.git",
270 | "reference": "69568e4293f4fa993f3b0e51c9723e1e17c41379"
271 | },
272 | "dist": {
273 | "type": "zip",
274 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/69568e4293f4fa993f3b0e51c9723e1e17c41379",
275 | "reference": "69568e4293f4fa993f3b0e51c9723e1e17c41379",
276 | "shasum": ""
277 | },
278 | "require": {
279 | "php": "^7.2.5 || ^8.0",
280 | "psr/http-factory": "^1.0",
281 | "psr/http-message": "^1.0",
282 | "ralouphie/getallheaders": "^3.0"
283 | },
284 | "provide": {
285 | "psr/http-factory-implementation": "1.0",
286 | "psr/http-message-implementation": "1.0"
287 | },
288 | "require-dev": {
289 | "bamarni/composer-bin-plugin": "^1.8.1",
290 | "http-interop/http-factory-tests": "^0.9",
291 | "phpunit/phpunit": "^8.5.29 || ^9.5.23"
292 | },
293 | "suggest": {
294 | "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
295 | },
296 | "type": "library",
297 | "extra": {
298 | "bamarni-bin": {
299 | "bin-links": true,
300 | "forward-command": false
301 | },
302 | "branch-alias": {
303 | "dev-master": "2.4-dev"
304 | }
305 | },
306 | "autoload": {
307 | "psr-4": {
308 | "GuzzleHttp\\Psr7\\": "src/"
309 | }
310 | },
311 | "notification-url": "https://packagist.org/downloads/",
312 | "license": [
313 | "MIT"
314 | ],
315 | "authors": [
316 | {
317 | "name": "Graham Campbell",
318 | "email": "hello@gjcampbell.co.uk",
319 | "homepage": "https://github.com/GrahamCampbell"
320 | },
321 | {
322 | "name": "Michael Dowling",
323 | "email": "mtdowling@gmail.com",
324 | "homepage": "https://github.com/mtdowling"
325 | },
326 | {
327 | "name": "George Mponos",
328 | "email": "gmponos@gmail.com",
329 | "homepage": "https://github.com/gmponos"
330 | },
331 | {
332 | "name": "Tobias Nyholm",
333 | "email": "tobias.nyholm@gmail.com",
334 | "homepage": "https://github.com/Nyholm"
335 | },
336 | {
337 | "name": "Márk Sági-Kazár",
338 | "email": "mark.sagikazar@gmail.com",
339 | "homepage": "https://github.com/sagikazarmark"
340 | },
341 | {
342 | "name": "Tobias Schultze",
343 | "email": "webmaster@tubo-world.de",
344 | "homepage": "https://github.com/Tobion"
345 | },
346 | {
347 | "name": "Márk Sági-Kazár",
348 | "email": "mark.sagikazar@gmail.com",
349 | "homepage": "https://sagikazarmark.hu"
350 | }
351 | ],
352 | "description": "PSR-7 message implementation that also provides common utility methods",
353 | "keywords": [
354 | "http",
355 | "message",
356 | "psr-7",
357 | "request",
358 | "response",
359 | "stream",
360 | "uri",
361 | "url"
362 | ],
363 | "support": {
364 | "issues": "https://github.com/guzzle/psr7/issues",
365 | "source": "https://github.com/guzzle/psr7/tree/2.4.1"
366 | },
367 | "funding": [
368 | {
369 | "url": "https://github.com/GrahamCampbell",
370 | "type": "github"
371 | },
372 | {
373 | "url": "https://github.com/Nyholm",
374 | "type": "github"
375 | },
376 | {
377 | "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
378 | "type": "tidelift"
379 | }
380 | ],
381 | "time": "2022-08-28T14:45:39+00:00"
382 | },
383 | {
384 | "name": "psr/http-client",
385 | "version": "1.0.1",
386 | "source": {
387 | "type": "git",
388 | "url": "https://github.com/php-fig/http-client.git",
389 | "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
390 | },
391 | "dist": {
392 | "type": "zip",
393 | "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
394 | "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
395 | "shasum": ""
396 | },
397 | "require": {
398 | "php": "^7.0 || ^8.0",
399 | "psr/http-message": "^1.0"
400 | },
401 | "type": "library",
402 | "extra": {
403 | "branch-alias": {
404 | "dev-master": "1.0.x-dev"
405 | }
406 | },
407 | "autoload": {
408 | "psr-4": {
409 | "Psr\\Http\\Client\\": "src/"
410 | }
411 | },
412 | "notification-url": "https://packagist.org/downloads/",
413 | "license": [
414 | "MIT"
415 | ],
416 | "authors": [
417 | {
418 | "name": "PHP-FIG",
419 | "homepage": "http://www.php-fig.org/"
420 | }
421 | ],
422 | "description": "Common interface for HTTP clients",
423 | "homepage": "https://github.com/php-fig/http-client",
424 | "keywords": [
425 | "http",
426 | "http-client",
427 | "psr",
428 | "psr-18"
429 | ],
430 | "support": {
431 | "source": "https://github.com/php-fig/http-client/tree/master"
432 | },
433 | "time": "2020-06-29T06:28:15+00:00"
434 | },
435 | {
436 | "name": "psr/http-factory",
437 | "version": "1.0.1",
438 | "source": {
439 | "type": "git",
440 | "url": "https://github.com/php-fig/http-factory.git",
441 | "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
442 | },
443 | "dist": {
444 | "type": "zip",
445 | "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
446 | "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
447 | "shasum": ""
448 | },
449 | "require": {
450 | "php": ">=7.0.0",
451 | "psr/http-message": "^1.0"
452 | },
453 | "type": "library",
454 | "extra": {
455 | "branch-alias": {
456 | "dev-master": "1.0.x-dev"
457 | }
458 | },
459 | "autoload": {
460 | "psr-4": {
461 | "Psr\\Http\\Message\\": "src/"
462 | }
463 | },
464 | "notification-url": "https://packagist.org/downloads/",
465 | "license": [
466 | "MIT"
467 | ],
468 | "authors": [
469 | {
470 | "name": "PHP-FIG",
471 | "homepage": "http://www.php-fig.org/"
472 | }
473 | ],
474 | "description": "Common interfaces for PSR-7 HTTP message factories",
475 | "keywords": [
476 | "factory",
477 | "http",
478 | "message",
479 | "psr",
480 | "psr-17",
481 | "psr-7",
482 | "request",
483 | "response"
484 | ],
485 | "support": {
486 | "source": "https://github.com/php-fig/http-factory/tree/master"
487 | },
488 | "time": "2019-04-30T12:38:16+00:00"
489 | },
490 | {
491 | "name": "psr/http-message",
492 | "version": "1.0.1",
493 | "source": {
494 | "type": "git",
495 | "url": "https://github.com/php-fig/http-message.git",
496 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
497 | },
498 | "dist": {
499 | "type": "zip",
500 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
501 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
502 | "shasum": ""
503 | },
504 | "require": {
505 | "php": ">=5.3.0"
506 | },
507 | "type": "library",
508 | "extra": {
509 | "branch-alias": {
510 | "dev-master": "1.0.x-dev"
511 | }
512 | },
513 | "autoload": {
514 | "psr-4": {
515 | "Psr\\Http\\Message\\": "src/"
516 | }
517 | },
518 | "notification-url": "https://packagist.org/downloads/",
519 | "license": [
520 | "MIT"
521 | ],
522 | "authors": [
523 | {
524 | "name": "PHP-FIG",
525 | "homepage": "http://www.php-fig.org/"
526 | }
527 | ],
528 | "description": "Common interface for HTTP messages",
529 | "homepage": "https://github.com/php-fig/http-message",
530 | "keywords": [
531 | "http",
532 | "http-message",
533 | "psr",
534 | "psr-7",
535 | "request",
536 | "response"
537 | ],
538 | "support": {
539 | "source": "https://github.com/php-fig/http-message/tree/master"
540 | },
541 | "time": "2016-08-06T14:39:51+00:00"
542 | },
543 | {
544 | "name": "ralouphie/getallheaders",
545 | "version": "3.0.3",
546 | "source": {
547 | "type": "git",
548 | "url": "https://github.com/ralouphie/getallheaders.git",
549 | "reference": "120b605dfeb996808c31b6477290a714d356e822"
550 | },
551 | "dist": {
552 | "type": "zip",
553 | "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
554 | "reference": "120b605dfeb996808c31b6477290a714d356e822",
555 | "shasum": ""
556 | },
557 | "require": {
558 | "php": ">=5.6"
559 | },
560 | "require-dev": {
561 | "php-coveralls/php-coveralls": "^2.1",
562 | "phpunit/phpunit": "^5 || ^6.5"
563 | },
564 | "type": "library",
565 | "autoload": {
566 | "files": [
567 | "src/getallheaders.php"
568 | ]
569 | },
570 | "notification-url": "https://packagist.org/downloads/",
571 | "license": [
572 | "MIT"
573 | ],
574 | "authors": [
575 | {
576 | "name": "Ralph Khattar",
577 | "email": "ralph.khattar@gmail.com"
578 | }
579 | ],
580 | "description": "A polyfill for getallheaders.",
581 | "support": {
582 | "issues": "https://github.com/ralouphie/getallheaders/issues",
583 | "source": "https://github.com/ralouphie/getallheaders/tree/develop"
584 | },
585 | "time": "2019-03-08T08:55:37+00:00"
586 | },
587 | {
588 | "name": "symfony/deprecation-contracts",
589 | "version": "v2.5.2",
590 | "source": {
591 | "type": "git",
592 | "url": "https://github.com/symfony/deprecation-contracts.git",
593 | "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
594 | },
595 | "dist": {
596 | "type": "zip",
597 | "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
598 | "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
599 | "shasum": ""
600 | },
601 | "require": {
602 | "php": ">=7.1"
603 | },
604 | "type": "library",
605 | "extra": {
606 | "branch-alias": {
607 | "dev-main": "2.5-dev"
608 | },
609 | "thanks": {
610 | "name": "symfony/contracts",
611 | "url": "https://github.com/symfony/contracts"
612 | }
613 | },
614 | "autoload": {
615 | "files": [
616 | "function.php"
617 | ]
618 | },
619 | "notification-url": "https://packagist.org/downloads/",
620 | "license": [
621 | "MIT"
622 | ],
623 | "authors": [
624 | {
625 | "name": "Nicolas Grekas",
626 | "email": "p@tchwork.com"
627 | },
628 | {
629 | "name": "Symfony Community",
630 | "homepage": "https://symfony.com/contributors"
631 | }
632 | ],
633 | "description": "A generic function and convention to trigger deprecation notices",
634 | "homepage": "https://symfony.com",
635 | "support": {
636 | "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2"
637 | },
638 | "funding": [
639 | {
640 | "url": "https://symfony.com/sponsor",
641 | "type": "custom"
642 | },
643 | {
644 | "url": "https://github.com/fabpot",
645 | "type": "github"
646 | },
647 | {
648 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
649 | "type": "tidelift"
650 | }
651 | ],
652 | "time": "2022-01-02T09:53:40+00:00"
653 | }
654 | ],
655 | "packages-dev": [],
656 | "aliases": [],
657 | "minimum-stability": "stable",
658 | "stability-flags": [],
659 | "prefer-stable": false,
660 | "prefer-lowest": false,
661 | "platform": [],
662 | "platform-dev": [],
663 | "plugin-api-version": "2.3.0"
664 | }
665 |
--------------------------------------------------------------------------------
/js/gdrive.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 | var backendId = 'googledrive';
3 | var backendUrl = OC.generateUrl('apps/files_external_gdrive/oauth');
4 |
5 | function generateUrl() {
6 | // no mapping between client ID and Google 'project', so we always load the same URL
7 | return 'https://console.developers.google.com/';
8 | }
9 |
10 | OCA.External.Settings.mountConfig.whenSelectBackend(function($tr, backend, onCompletion) {
11 | if (backend === backendId) {
12 | var backendEl = $tr.find('.backend');
13 | var el = $(document.createElement('a'))
14 | .attr('href', generateUrl($tr))
15 | .attr('target', '_blank')
16 | .attr('title', t('files_external_gdrive', 'Google Drive App Configuration'))
17 | .addClass('icon-settings svg')
18 | ;
19 | el.on('click', function(event) {
20 | var a = $(event.target);
21 | a.attr('href', generateUrl($(this).closest('tr')));
22 | });
23 | el.tooltip({placement: 'top'});
24 | backendEl.append(el);
25 |
26 | /**
27 | * ------- OAUTH2 Events ----------
28 | *
29 | * The files_external_{backendId} app's CUSTOM JS should handle the OAuth2 events itself
30 | * instead on relying on the core for the implemention. These two functions
31 | * [1] OCA.External.Settings.OAuth2.getAuthUrl, [2] OCA.External.Settings.OAuth2.verifyCode
32 | * abstract away the details of sending the request to backend for getting the AuthURL or
33 | * verifying the code, mounting the storage config etc
34 | */
35 | $tr.on('oauth_step1', '.configuration', function (event, data) {
36 | if (data['backend_id'] !== backendId) {
37 | return; // means the trigger is not for this storage adapter
38 | }
39 |
40 | // Redirects the User on success else displays an alert (with error message sent by backend)
41 | OCA.External.Settings.OAuth2.getAuthUrl(backendUrl, data);
42 | });
43 |
44 | $tr.on('oauth_step2', '.configuration', function (event, data) {
45 | if (data['backend_id'] !== backendId || data['code'] === undefined) {
46 | return; // means the trigger is not for this OAuth2 grant
47 | }
48 |
49 | OCA.External.Settings.OAuth2.verifyCode(backendUrl, data)
50 | .fail(function (message) {
51 | OC.dialogs.alert(message,
52 | t('files_external_gdrive', 'Error verifying OAuth2 Code for ' + backendId)
53 | );
54 | });
55 | });
56 | }
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/l10n/.tx/config:
--------------------------------------------------------------------------------
1 | [main]
2 | host = https://www.transifex.com
3 | lang_map = ja_JP: ja
4 |
5 | [o:owncloud-org:p:owncloud:r:files_external_gdrive]
6 | file_filter = /files_external_gdrive.po
7 | source_file = templates/files_external_gdrive.pot
8 | source_lang = en
9 | type = PO
10 |
--------------------------------------------------------------------------------
/l10n/af_ZA.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive Toepopstelling",
5 | "Error verifying OAuth2 Code for " : "Fout tydens OAuth2-kodeverifiëring vir",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Stap 1 het misluk. Uitsondering: %s",
8 | "Step 2 failed. Exception: %s" : "Stap 2 het misluk. Uitsondering: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/af_ZA.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive Toepopstelling",
3 | "Error verifying OAuth2 Code for " : "Fout tydens OAuth2-kodeverifiëring vir",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Stap 1 het misluk. Uitsondering: %s",
6 | "Step 2 failed. Exception: %s" : "Stap 2 het misluk. Uitsondering: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/ar.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "تكوين تطبيق Google Drive",
5 | "Error verifying OAuth2 Code for " : "خطأ في التحقق من صحة رمز OAuth2 لـ",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "فشلت الخطوة 1. استثناء: %s",
8 | "Step 2 failed. Exception: %s" : "فشلت الخطوة 2. استثناء: %s"
9 | },
10 | "nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;");
11 |
--------------------------------------------------------------------------------
/l10n/ar.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "تكوين تطبيق Google Drive",
3 | "Error verifying OAuth2 Code for " : "خطأ في التحقق من صحة رمز OAuth2 لـ",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "فشلت الخطوة 1. استثناء: %s",
6 | "Step 2 failed. Exception: %s" : "فشلت الخطوة 2. استثناء: %s"
7 | },"pluralForm" :"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;"
8 | }
--------------------------------------------------------------------------------
/l10n/ast.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Configuración d'Aplicación Google Drive",
5 | "Error verifying OAuth2 Code for " : "Fallu verificando'l códigu d'OAuth pa",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Pasu 1 fallíu. Esceición: %s",
8 | "Step 2 failed. Exception: %s" : "Pasu 2 fallíu. Esceición: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/ast.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Configuración d'Aplicación Google Drive",
3 | "Error verifying OAuth2 Code for " : "Fallu verificando'l códigu d'OAuth pa",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Pasu 1 fallíu. Esceición: %s",
6 | "Step 2 failed. Exception: %s" : "Pasu 2 fallíu. Esceición: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/bg_BG.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Настройка на приложението Google Drive",
5 | "Error verifying OAuth2 Code for " : "Грешка при проверката на кода OAuth2 за ",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Стъпка 1 е неуспешна. Изключение: %s",
8 | "Step 2 failed. Exception: %s" : "Стъпка 2 е неуспешна. Изключение: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/bg_BG.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Настройка на приложението Google Drive",
3 | "Error verifying OAuth2 Code for " : "Грешка при проверката на кода OAuth2 за ",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Стъпка 1 е неуспешна. Изключение: %s",
6 | "Step 2 failed. Exception: %s" : "Стъпка 2 е неуспешна. Изключение: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/ca.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Configuració de l'aplicatiu Google Drive",
5 | "Error verifying OAuth2 Code for " : "Error verificant el codi OAuth2 ",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "El pas 1 ha fallat. Excepció: %s",
8 | "Step 2 failed. Exception: %s" : "El pas 2 ha fallat. Excepció: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/ca.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Configuració de l'aplicatiu Google Drive",
3 | "Error verifying OAuth2 Code for " : "Error verificant el codi OAuth2 ",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "El pas 1 ha fallat. Excepció: %s",
6 | "Step 2 failed. Exception: %s" : "El pas 2 ha fallat. Excepció: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/cs_CZ.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Nastavení aplikace Disk Google",
5 | "Error verifying OAuth2 Code for " : "Chyba ověření OAuth2 kódu pro",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Selhal krok 1. Výjimka: %s",
8 | "Step 2 failed. Exception: %s" : "Selhal krok 2. Výjimka: %s"
9 | },
10 | "nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;");
11 |
--------------------------------------------------------------------------------
/l10n/cs_CZ.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Nastavení aplikace Disk Google",
3 | "Error verifying OAuth2 Code for " : "Chyba ověření OAuth2 kódu pro",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Selhal krok 1. Výjimka: %s",
6 | "Step 2 failed. Exception: %s" : "Selhal krok 2. Výjimka: %s"
7 | },"pluralForm" :"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;"
8 | }
--------------------------------------------------------------------------------
/l10n/da.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive App konfiguration",
5 | "Error verifying OAuth2 Code for " : "Fejl ved verifikation af OAuth2 code for",
6 | "Google Drive" : "Google Drev",
7 | "Step 1 failed. Exception: %s" : "Trin 1 mislykkedes. Undtagelse: %s",
8 | "Step 2 failed. Exception: %s" : "Trin 2 mislykkedes. Undtagelse: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/da.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive App konfiguration",
3 | "Error verifying OAuth2 Code for " : "Fejl ved verifikation af OAuth2 code for",
4 | "Google Drive" : "Google Drev",
5 | "Step 1 failed. Exception: %s" : "Trin 1 mislykkedes. Undtagelse: %s",
6 | "Step 2 failed. Exception: %s" : "Trin 2 mislykkedes. Undtagelse: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/de.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive - App Konfiguration",
5 | "Error verifying OAuth2 Code for " : "OAuth2-Code Überprüfungsfehler bei",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Schritt 1 fehlgeschlagen. Fehlermeldung: %s",
8 | "Step 2 failed. Exception: %s" : "Schritt 2 fehlgeschlagen. Fehlermeldung: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/de.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive - App Konfiguration",
3 | "Error verifying OAuth2 Code for " : "OAuth2-Code Überprüfungsfehler bei",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Schritt 1 fehlgeschlagen. Fehlermeldung: %s",
6 | "Step 2 failed. Exception: %s" : "Schritt 2 fehlgeschlagen. Fehlermeldung: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/de_AT.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive App Konfigurierung",
5 | "Error verifying OAuth2 Code for " : "Bei der Verifikation des OAuth2 Codes für [ ] ist ein Fehler aufgetreten.",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Schritt 1 ist fehlgeschlagen. Ausnahme: %s",
8 | "Step 2 failed. Exception: %s" : "Schritt 2 ist fehlgeschlagen. Ausnahme: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/de_AT.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive App Konfigurierung",
3 | "Error verifying OAuth2 Code for " : "Bei der Verifikation des OAuth2 Codes für [ ] ist ein Fehler aufgetreten.",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Schritt 1 ist fehlgeschlagen. Ausnahme: %s",
6 | "Step 2 failed. Exception: %s" : "Schritt 2 ist fehlgeschlagen. Ausnahme: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/de_CH.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive - App Konfiguration",
5 | "Error verifying OAuth2 Code for " : "OAuth2-Code Überprüfungsfehler bei",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Schritt 1 fehlgeschlagen. Fehlermeldung: %s",
8 | "Step 2 failed. Exception: %s" : "Schritt 2 fehlgeschlagen. Fehlermeldung: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/de_CH.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive - App Konfiguration",
3 | "Error verifying OAuth2 Code for " : "OAuth2-Code Überprüfungsfehler bei",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Schritt 1 fehlgeschlagen. Fehlermeldung: %s",
6 | "Step 2 failed. Exception: %s" : "Schritt 2 fehlgeschlagen. Fehlermeldung: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/de_DE.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive - App Konfiguration",
5 | "Error verifying OAuth2 Code for " : "OAuth2-Code Überprüfungsfehler bei",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Schritt 1 fehlgeschlagen. Fehlermeldung: %s",
8 | "Step 2 failed. Exception: %s" : "Schritt 2 fehlgeschlagen. Fehlermeldung: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/de_DE.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive - App Konfiguration",
3 | "Error verifying OAuth2 Code for " : "OAuth2-Code Überprüfungsfehler bei",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Schritt 1 fehlgeschlagen. Fehlermeldung: %s",
6 | "Step 2 failed. Exception: %s" : "Schritt 2 fehlgeschlagen. Fehlermeldung: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/el.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Ρυθμίσεις εφαρμογής Google Drive",
5 | "Error verifying OAuth2 Code for " : "Σφάλμα κατά την επαλήθευση του κώδικα OAuth2 για",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Το βήμα 1 απέτυχε. Εξαίρεση: %s",
8 | "Step 2 failed. Exception: %s" : "Το βήμα 2 απέτυχε. Εξαίρεση: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/el.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Ρυθμίσεις εφαρμογής Google Drive",
3 | "Error verifying OAuth2 Code for " : "Σφάλμα κατά την επαλήθευση του κώδικα OAuth2 για",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Το βήμα 1 απέτυχε. Εξαίρεση: %s",
6 | "Step 2 failed. Exception: %s" : "Το βήμα 2 απέτυχε. Εξαίρεση: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/en_GB.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive App Configuration",
5 | "Error verifying OAuth2 Code for " : "Error verifying OAuth2 Code for ",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Step 1 failed. Exception: %s",
8 | "Step 2 failed. Exception: %s" : "Step 2 failed. Exception: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/en_GB.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive App Configuration",
3 | "Error verifying OAuth2 Code for " : "Error verifying OAuth2 Code for ",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Step 1 failed. Exception: %s",
6 | "Step 2 failed. Exception: %s" : "Step 2 failed. Exception: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/es.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Configuración de la app de Google Drive",
5 | "Error verifying OAuth2 Code for " : "Error verificando el código OAuth2 para",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "El paso 1 falló. Excepción: %s",
8 | "Step 2 failed. Exception: %s" : "El paso 2 falló. Excepción: %s"
9 | },
10 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
11 |
--------------------------------------------------------------------------------
/l10n/es.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Configuración de la app de Google Drive",
3 | "Error verifying OAuth2 Code for " : "Error verificando el código OAuth2 para",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "El paso 1 falló. Excepción: %s",
6 | "Step 2 failed. Exception: %s" : "El paso 2 falló. Excepción: %s"
7 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
8 | }
--------------------------------------------------------------------------------
/l10n/es_AR.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Configuración de Aplicación Google Drive",
5 | "Error verifying OAuth2 Code for " : "Error verificando el código para Oauth2",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Error en paso 1. Excepción: %s",
8 | "Step 2 failed. Exception: %s" : "Error en paso 2. Excepción: %s"
9 | },
10 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
11 |
--------------------------------------------------------------------------------
/l10n/es_AR.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Configuración de Aplicación Google Drive",
3 | "Error verifying OAuth2 Code for " : "Error verificando el código para Oauth2",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Error en paso 1. Excepción: %s",
6 | "Step 2 failed. Exception: %s" : "Error en paso 2. Excepción: %s"
7 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
8 | }
--------------------------------------------------------------------------------
/l10n/es_MX.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Configuración de la Aplicación Google Drive",
5 | "Google Drive" : "Google Drive",
6 | "Step 1 failed. Exception: %s" : "Paso 1 falló. Excepción: %s",
7 | "Step 2 failed. Exception: %s" : "Paso 2 falló. Excepción: %s"
8 | },
9 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
10 |
--------------------------------------------------------------------------------
/l10n/es_MX.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Configuración de la Aplicación Google Drive",
3 | "Google Drive" : "Google Drive",
4 | "Step 1 failed. Exception: %s" : "Paso 1 falló. Excepción: %s",
5 | "Step 2 failed. Exception: %s" : "Paso 2 falló. Excepción: %s"
6 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
7 | }
--------------------------------------------------------------------------------
/l10n/eu.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive App Ezarpenak",
5 | "Error verifying OAuth2 Code for " : "Errorea gertatu da OAuth2 Kodea egiaztatzean honentzat",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "1 Urratsak huts egin du. Salbuespena: %s",
8 | "Step 2 failed. Exception: %s" : "2 Urratsak huts egin du. Salbuespena: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/eu.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive App Ezarpenak",
3 | "Error verifying OAuth2 Code for " : "Errorea gertatu da OAuth2 Kodea egiaztatzean honentzat",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "1 Urratsak huts egin du. Salbuespena: %s",
6 | "Step 2 failed. Exception: %s" : "2 Urratsak huts egin du. Salbuespena: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/fa.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Error verifying OAuth2 Code for " : "خطا در تأیید رمز OAuth2 برای",
5 | "Google Drive" : "Google Drive",
6 | "Step 1 failed. Exception: %s" : "گام 1 ناموفق بود. خطا: %s",
7 | "Step 2 failed. Exception: %s" : "گام 2ناموفق بود. خطا: %s"
8 | },
9 | "nplurals=2; plural=(n > 1);");
10 |
--------------------------------------------------------------------------------
/l10n/fa.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Error verifying OAuth2 Code for " : "خطا در تأیید رمز OAuth2 برای",
3 | "Google Drive" : "Google Drive",
4 | "Step 1 failed. Exception: %s" : "گام 1 ناموفق بود. خطا: %s",
5 | "Step 2 failed. Exception: %s" : "گام 2ناموفق بود. خطا: %s"
6 | },"pluralForm" :"nplurals=2; plural=(n > 1);"
7 | }
--------------------------------------------------------------------------------
/l10n/fi_FI.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive -sovelluksen määritys",
5 | "Google Drive" : "Google Drive",
6 | "Step 1 failed. Exception: %s" : "Vaihe 1 epäonnistui. Poikkeus: %s",
7 | "Step 2 failed. Exception: %s" : "Vaihe 2 epäonnistui. Poikkeus: %s"
8 | },
9 | "nplurals=2; plural=(n != 1);");
10 |
--------------------------------------------------------------------------------
/l10n/fi_FI.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive -sovelluksen määritys",
3 | "Google Drive" : "Google Drive",
4 | "Step 1 failed. Exception: %s" : "Vaihe 1 epäonnistui. Poikkeus: %s",
5 | "Step 2 failed. Exception: %s" : "Vaihe 2 epäonnistui. Poikkeus: %s"
6 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
7 | }
--------------------------------------------------------------------------------
/l10n/fr.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Configuration de l'application Google Drive",
5 | "Error verifying OAuth2 Code for " : "Erreur de vérification du code OAuth2 pour",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "L’étape 1 a échoué. Erreur : %s",
8 | "Step 2 failed. Exception: %s" : "L’étape 2 a échoué. Erreur : %s"
9 | },
10 | "nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
11 |
--------------------------------------------------------------------------------
/l10n/fr.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Configuration de l'application Google Drive",
3 | "Error verifying OAuth2 Code for " : "Erreur de vérification du code OAuth2 pour",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "L’étape 1 a échoué. Erreur : %s",
6 | "Step 2 failed. Exception: %s" : "L’étape 2 a échoué. Erreur : %s"
7 | },"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
8 | }
--------------------------------------------------------------------------------
/l10n/gl.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Configuración da aplicación do Gogle Drive",
5 | "Error verifying OAuth2 Code for " : "Produciuse un erro a verificar o código OAuth2 para",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Fallou o paso 1. Excepción: %s",
8 | "Step 2 failed. Exception: %s" : "Fallou o paso 2. Excepción: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/gl.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Configuración da aplicación do Gogle Drive",
3 | "Error verifying OAuth2 Code for " : "Produciuse un erro a verificar o código OAuth2 para",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Fallou o paso 1. Excepción: %s",
6 | "Step 2 failed. Exception: %s" : "Fallou o paso 2. Excepción: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/he.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "הגדרות יישום גוגל דרייב",
5 | "Error verifying OAuth2 Code for " : "שגיאה באימות קוד OAuth2 עבור ",
6 | "Google Drive" : "גוגל דרייב",
7 | "Step 1 failed. Exception: %s" : "שלב 1 נכשל. חריג: %s",
8 | "Step 2 failed. Exception: %s" : "שלב 2 נכשל. חריג: %s"
9 | },
10 | "nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: 2;");
11 |
--------------------------------------------------------------------------------
/l10n/he.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "הגדרות יישום גוגל דרייב",
3 | "Error verifying OAuth2 Code for " : "שגיאה באימות קוד OAuth2 עבור ",
4 | "Google Drive" : "גוגל דרייב",
5 | "Step 1 failed. Exception: %s" : "שלב 1 נכשל. חריג: %s",
6 | "Step 2 failed. Exception: %s" : "שלב 2 נכשל. חריג: %s"
7 | },"pluralForm" :"nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: 2;"
8 | }
--------------------------------------------------------------------------------
/l10n/hu_HU.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive Alkalmazáskonfiguráció",
5 | "Error verifying OAuth2 Code for " : "Hiba az OAuth2 kód ellenőrzése miatt",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "1. lépés sikertelen. Kivétel:%s",
8 | "Step 2 failed. Exception: %s" : "A 2. lépés nem sikerült. Kivétel:%s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/hu_HU.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive Alkalmazáskonfiguráció",
3 | "Error verifying OAuth2 Code for " : "Hiba az OAuth2 kód ellenőrzése miatt",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "1. lépés sikertelen. Kivétel:%s",
6 | "Step 2 failed. Exception: %s" : "A 2. lépés nem sikerült. Kivétel:%s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/id.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Konfigurasi aplikasi Google Drive ",
5 | "Error verifying OAuth2 Code for " : "Gagal memverifikasi Kode OAuth2 untuk",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Langkah 1 gagal. Kecuali: %s",
8 | "Step 2 failed. Exception: %s" : "Langkah 2 gagal. Kecuali: %s"
9 | },
10 | "nplurals=1; plural=0;");
11 |
--------------------------------------------------------------------------------
/l10n/id.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Konfigurasi aplikasi Google Drive ",
3 | "Error verifying OAuth2 Code for " : "Gagal memverifikasi Kode OAuth2 untuk",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Langkah 1 gagal. Kecuali: %s",
6 | "Step 2 failed. Exception: %s" : "Langkah 2 gagal. Kecuali: %s"
7 | },"pluralForm" :"nplurals=1; plural=0;"
8 | }
--------------------------------------------------------------------------------
/l10n/is.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Uppsetning Google Drive forrits",
5 | "Error verifying OAuth2 Code for " : "Villa við að sannvotta OAuth2-kóða fyrir",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Skref 1 mistókst. Undantekning: %s",
8 | "Step 2 failed. Exception: %s" : "Skref 2 mistókst. Undantekning: %s"
9 | },
10 | "nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);");
11 |
--------------------------------------------------------------------------------
/l10n/is.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Uppsetning Google Drive forrits",
3 | "Error verifying OAuth2 Code for " : "Villa við að sannvotta OAuth2-kóða fyrir",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Skref 1 mistókst. Undantekning: %s",
6 | "Step 2 failed. Exception: %s" : "Skref 2 mistókst. Undantekning: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);"
8 | }
--------------------------------------------------------------------------------
/l10n/it.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Configurazione applicazione Google Drive",
5 | "Error verifying OAuth2 Code for " : "Errore di verifica del OAuth2 Code per",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Fase 1 non riuscita. Eccezione: %s",
8 | "Step 2 failed. Exception: %s" : "Fase 2 non riuscita. Eccezione: %s"
9 | },
10 | "nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
11 |
--------------------------------------------------------------------------------
/l10n/it.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Configurazione applicazione Google Drive",
3 | "Error verifying OAuth2 Code for " : "Errore di verifica del OAuth2 Code per",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Fase 1 non riuscita. Eccezione: %s",
6 | "Step 2 failed. Exception: %s" : "Fase 2 non riuscita. Eccezione: %s"
7 | },"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
8 | }
--------------------------------------------------------------------------------
/l10n/ja.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google アプリ設定",
5 | "Error verifying OAuth2 Code for " : "のOAuth2コードで検証エラーです。",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "ステップ 1 の実行に失敗しました。例外: %s",
8 | "Step 2 failed. Exception: %s" : "ステップ 2 の実行に失敗しました。例外: %s"
9 | },
10 | "nplurals=1; plural=0;");
11 |
--------------------------------------------------------------------------------
/l10n/ja.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google アプリ設定",
3 | "Error verifying OAuth2 Code for " : "のOAuth2コードで検証エラーです。",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "ステップ 1 の実行に失敗しました。例外: %s",
6 | "Step 2 failed. Exception: %s" : "ステップ 2 の実行に失敗しました。例外: %s"
7 | },"pluralForm" :"nplurals=1; plural=0;"
8 | }
--------------------------------------------------------------------------------
/l10n/ko.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google 드라이브 앱 설정",
5 | "Error verifying OAuth2 Code for " : "다음의 OAuth2 코드 검증 오류",
6 | "Google Drive" : "Google 드라이브",
7 | "Step 1 failed. Exception: %s" : "1단계 실패. 예외: %s",
8 | "Step 2 failed. Exception: %s" : "2단계 실패. 예외: %s"
9 | },
10 | "nplurals=1; plural=0;");
11 |
--------------------------------------------------------------------------------
/l10n/ko.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google 드라이브 앱 설정",
3 | "Error verifying OAuth2 Code for " : "다음의 OAuth2 코드 검증 오류",
4 | "Google Drive" : "Google 드라이브",
5 | "Step 1 failed. Exception: %s" : "1단계 실패. 예외: %s",
6 | "Step 2 failed. Exception: %s" : "2단계 실패. 예외: %s"
7 | },"pluralForm" :"nplurals=1; plural=0;"
8 | }
--------------------------------------------------------------------------------
/l10n/lt_LT.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google disko programėlės konfigūracija",
5 | "Error verifying OAuth2 Code for " : "Klaida, patvirtinant OAuth2 kodą, skirtą ",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "1 žingsnio klaida: %s",
8 | "Step 2 failed. Exception: %s" : "2 žingsnio klaida: %s"
9 | },
10 | "nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);");
11 |
--------------------------------------------------------------------------------
/l10n/lt_LT.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google disko programėlės konfigūracija",
3 | "Error verifying OAuth2 Code for " : "Klaida, patvirtinant OAuth2 kodą, skirtą ",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "1 žingsnio klaida: %s",
6 | "Step 2 failed. Exception: %s" : "2 žingsnio klaida: %s"
7 | },"pluralForm" :"nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);"
8 | }
--------------------------------------------------------------------------------
/l10n/lv.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive Lietotnes Konfigurācija",
5 | "Error verifying OAuth2 Code for " : "Kļūda verificējot OAuth2 kodu priekš ",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Pirmais solis neizdevās. Izņēmums: %s",
8 | "Step 2 failed. Exception: %s" : "Otrais solis neizdevās. Izņēmums: %s"
9 | },
10 | "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);");
11 |
--------------------------------------------------------------------------------
/l10n/lv.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive Lietotnes Konfigurācija",
3 | "Error verifying OAuth2 Code for " : "Kļūda verificējot OAuth2 kodu priekš ",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Pirmais solis neizdevās. Izņēmums: %s",
6 | "Step 2 failed. Exception: %s" : "Otrais solis neizdevās. Izņēmums: %s"
7 | },"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);"
8 | }
--------------------------------------------------------------------------------
/l10n/mk.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive Конфигурација",
5 | "Error verifying OAuth2 Code for " : "Грешка при верификација на OAuth2 кодот за",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Чекор 1 Неуспешен: %s",
8 | "Step 2 failed. Exception: %s" : "Чекор 2 Неуспешен: %s"
9 | },
10 | "nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;");
11 |
--------------------------------------------------------------------------------
/l10n/mk.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive Конфигурација",
3 | "Error verifying OAuth2 Code for " : "Грешка при верификација на OAuth2 кодот за",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Чекор 1 Неуспешен: %s",
6 | "Step 2 failed. Exception: %s" : "Чекор 2 Неуспешен: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;"
8 | }
--------------------------------------------------------------------------------
/l10n/nb_NO.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Konfigurering av Google Disk-app",
5 | "Error verifying OAuth2 Code for " : "Feil ved verifisering av OAuth kode for",
6 | "Google Drive" : "Google Disk",
7 | "Step 1 failed. Exception: %s" : "Steg 1 feilet. Unntak: %s",
8 | "Step 2 failed. Exception: %s" : "Steg 2 feilet. Unntak: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/nb_NO.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Konfigurering av Google Disk-app",
3 | "Error verifying OAuth2 Code for " : "Feil ved verifisering av OAuth kode for",
4 | "Google Drive" : "Google Disk",
5 | "Step 1 failed. Exception: %s" : "Steg 1 feilet. Unntak: %s",
6 | "Step 2 failed. Exception: %s" : "Steg 2 feilet. Unntak: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/nl.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "App-configuratie Google Drive",
5 | "Error verifying OAuth2 Code for " : "Fout bij het verifiëren van OAuth2-code voor",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Stap 1 is mislukt. Uitzondering: %s",
8 | "Step 2 failed. Exception: %s" : "Stap 2 is mislukt. Uitzondering: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/nl.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "App-configuratie Google Drive",
3 | "Error verifying OAuth2 Code for " : "Fout bij het verifiëren van OAuth2-code voor",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Stap 1 is mislukt. Uitzondering: %s",
6 | "Step 2 failed. Exception: %s" : "Stap 2 is mislukt. Uitzondering: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/nn_NO.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive program konfigurasjon",
5 | "Error verifying OAuth2 Code for " : "Feil ved verifisering av OAuth2 kode for",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Steg 1 feila. Feil: %s",
8 | "Step 2 failed. Exception: %s" : "Steg 2 feila. Feil: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/nn_NO.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive program konfigurasjon",
3 | "Error verifying OAuth2 Code for " : "Feil ved verifisering av OAuth2 kode for",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Steg 1 feila. Feil: %s",
6 | "Step 2 failed. Exception: %s" : "Steg 2 feila. Feil: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/oc.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Configuracion de l'aplicacion Google Drive",
5 | "Google Drive" : "Google Drive",
6 | "Step 1 failed. Exception: %s" : "L’etapa 1 a fracassat. Error : %s",
7 | "Step 2 failed. Exception: %s" : "L’etapa 2 a fracassat. Error : %s"
8 | },
9 | "nplurals=2; plural=(n > 1);");
10 |
--------------------------------------------------------------------------------
/l10n/oc.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Configuracion de l'aplicacion Google Drive",
3 | "Google Drive" : "Google Drive",
4 | "Step 1 failed. Exception: %s" : "L’etapa 1 a fracassat. Error : %s",
5 | "Step 2 failed. Exception: %s" : "L’etapa 2 a fracassat. Error : %s"
6 | },"pluralForm" :"nplurals=2; plural=(n > 1);"
7 | }
--------------------------------------------------------------------------------
/l10n/pl.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Konfiguracja aplikacji Google Drive",
5 | "Error verifying OAuth2 Code for " : "Błąd podczas weryfilacji kody OAuth2 dla",
6 | "Google Drive" : "Dysk Google",
7 | "Step 1 failed. Exception: %s" : "Krok 1 błędny. Błąd: %s",
8 | "Step 2 failed. Exception: %s" : "Krok 2 błędny. Błąd: %s"
9 | },
10 | "nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);");
11 |
--------------------------------------------------------------------------------
/l10n/pl.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Konfiguracja aplikacji Google Drive",
3 | "Error verifying OAuth2 Code for " : "Błąd podczas weryfilacji kody OAuth2 dla",
4 | "Google Drive" : "Dysk Google",
5 | "Step 1 failed. Exception: %s" : "Krok 1 błędny. Błąd: %s",
6 | "Step 2 failed. Exception: %s" : "Krok 2 błędny. Błąd: %s"
7 | },"pluralForm" :"nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);"
8 | }
--------------------------------------------------------------------------------
/l10n/pt_BR.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Configuração do Aplicativo Google Drive",
5 | "Error verifying OAuth2 Code for " : "Erro ao verificar o código OAuth2 de",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Passo 1 falhou. Exceção: %s",
8 | "Step 2 failed. Exception: %s" : "Passo 2 falhou. Exceção: %s"
9 | },
10 | "nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
11 |
--------------------------------------------------------------------------------
/l10n/pt_BR.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Configuração do Aplicativo Google Drive",
3 | "Error verifying OAuth2 Code for " : "Erro ao verificar o código OAuth2 de",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Passo 1 falhou. Exceção: %s",
6 | "Step 2 failed. Exception: %s" : "Passo 2 falhou. Exceção: %s"
7 | },"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
8 | }
--------------------------------------------------------------------------------
/l10n/pt_PT.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Configuração da aplicação Google Drive",
5 | "Error verifying OAuth2 Code for " : "Erro ao verificar o 'Código OAuth2' para",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Passo 1 falhou. Exceção: %s",
8 | "Step 2 failed. Exception: %s" : "Passo 2 falhou. Exceção: %s"
9 | },
10 | "nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
11 |
--------------------------------------------------------------------------------
/l10n/pt_PT.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Configuração da aplicação Google Drive",
3 | "Error verifying OAuth2 Code for " : "Erro ao verificar o 'Código OAuth2' para",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Passo 1 falhou. Exceção: %s",
6 | "Step 2 failed. Exception: %s" : "Passo 2 falhou. Exceção: %s"
7 | },"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
8 | }
--------------------------------------------------------------------------------
/l10n/ru.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Настройка приложения Google Drive",
5 | "Error verifying OAuth2 Code for " : "Ошибка проверки кода OAuth2 для ",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Шаг 1 неудачен. Исключение: %s",
8 | "Step 2 failed. Exception: %s" : "Шаг 2 неудачен. Исключение: %s"
9 | },
10 | "nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);");
11 |
--------------------------------------------------------------------------------
/l10n/ru.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Настройка приложения Google Drive",
3 | "Error verifying OAuth2 Code for " : "Ошибка проверки кода OAuth2 для ",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Шаг 1 неудачен. Исключение: %s",
6 | "Step 2 failed. Exception: %s" : "Шаг 2 неудачен. Исключение: %s"
7 | },"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"
8 | }
--------------------------------------------------------------------------------
/l10n/ru_RU.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Настройка приложения Google Drive",
5 | "Error verifying OAuth2 Code for " : "Ошибка проверки кода OAuth2 для ",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Шаг 1 неудачен. Исключение: %s",
8 | "Step 2 failed. Exception: %s" : "Шаг 2 неудачен. Исключение: %s"
9 | },
10 | "nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);");
11 |
--------------------------------------------------------------------------------
/l10n/ru_RU.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Настройка приложения Google Drive",
3 | "Error verifying OAuth2 Code for " : "Ошибка проверки кода OAuth2 для ",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Шаг 1 неудачен. Исключение: %s",
6 | "Step 2 failed. Exception: %s" : "Шаг 2 неудачен. Исключение: %s"
7 | },"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"
8 | }
--------------------------------------------------------------------------------
/l10n/sl.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Nastavitve programa Google Drive",
5 | "Error verifying OAuth2 Code for " : "Napaka overjanja kode OAuth2 za",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "1. korak je spodletel. Izjemna napaka: %s",
8 | "Step 2 failed. Exception: %s" : "2. korak je spodletel. Izjemna napaka: %s"
9 | },
10 | "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);");
11 |
--------------------------------------------------------------------------------
/l10n/sl.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Nastavitve programa Google Drive",
3 | "Error verifying OAuth2 Code for " : "Napaka overjanja kode OAuth2 za",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "1. korak je spodletel. Izjemna napaka: %s",
6 | "Step 2 failed. Exception: %s" : "2. korak je spodletel. Izjemna napaka: %s"
7 | },"pluralForm" :"nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);"
8 | }
--------------------------------------------------------------------------------
/l10n/sq.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Formësim i Aplikacionit Google Drive",
5 | "Error verifying OAuth2 Code for " : "Gabim në verifikimin e Kodit OAuth2 për ",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Hapi 1 dështoi. Përjashtim: %s",
8 | "Step 2 failed. Exception: %s" : "Hapi 2 dështoi. Përjashtim: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/sq.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Formësim i Aplikacionit Google Drive",
3 | "Error verifying OAuth2 Code for " : "Gabim në verifikimin e Kodit OAuth2 për ",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Hapi 1 dështoi. Përjashtim: %s",
6 | "Step 2 failed. Exception: %s" : "Hapi 2 dështoi. Përjashtim: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/sv.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive Programkonfiguration",
5 | "Error verifying OAuth2 Code for " : "Kunde inte verifiera OAuth2-kod för",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Steg 1 flaerade. Undantag: %s",
8 | "Step 2 failed. Exception: %s" : "Steg 2 falerade. Undantag: %s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/sv.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive Programkonfiguration",
3 | "Error verifying OAuth2 Code for " : "Kunde inte verifiera OAuth2-kod för",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Steg 1 flaerade. Undantag: %s",
6 | "Step 2 failed. Exception: %s" : "Steg 2 falerade. Undantag: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/th_TH.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "ตั้งค่าแอปฯ Google Drive",
5 | "Error verifying OAuth2 Code for " : "เกิดข้อผิดพลาดในการยืนยันรหัส OAuth2 สำหรับ",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "ขั้นตอนที่ 1 ล้มเหลว ข้อยกเว้น: %s",
8 | "Step 2 failed. Exception: %s" : "ขั้นตอนที่ 2 ล้มเหลว ข้อยกเว้น: %s"
9 | },
10 | "nplurals=1; plural=0;");
11 |
--------------------------------------------------------------------------------
/l10n/th_TH.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "ตั้งค่าแอปฯ Google Drive",
3 | "Error verifying OAuth2 Code for " : "เกิดข้อผิดพลาดในการยืนยันรหัส OAuth2 สำหรับ",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "ขั้นตอนที่ 1 ล้มเหลว ข้อยกเว้น: %s",
6 | "Step 2 failed. Exception: %s" : "ขั้นตอนที่ 2 ล้มเหลว ข้อยกเว้น: %s"
7 | },"pluralForm" :"nplurals=1; plural=0;"
8 | }
--------------------------------------------------------------------------------
/l10n/tr.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive Uygulama Yapılandırması",
5 | "Error verifying OAuth2 Code for " : "OAuth2 Kodu doğrulanırken hata oluştu",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "Adım 1 başarısız. İstisna: %s",
8 | "Step 2 failed. Exception: %s" : "Adım 2 başarısız. İstisna: %s"
9 | },
10 | "nplurals=2; plural=(n > 1);");
11 |
--------------------------------------------------------------------------------
/l10n/tr.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive Uygulama Yapılandırması",
3 | "Error verifying OAuth2 Code for " : "OAuth2 Kodu doğrulanırken hata oluştu",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "Adım 1 başarısız. İstisna: %s",
6 | "Step 2 failed. Exception: %s" : "Adım 2 başarısız. İstisna: %s"
7 | },"pluralForm" :"nplurals=2; plural=(n > 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/ug.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive ئەپ سەپلىمىسى",
5 | "Error verifying OAuth2 Code for " : "ئۈچۈن OAuth2 كودىنى دەلىللەشتە خاتالىق",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "1-قەدەم مەغلۇپ بولدى. مۇستەسنا:% s",
8 | "Step 2 failed. Exception: %s" : "ئىككىنچى قەدەم مەغلۇپ بولدى. مۇستەسنا:% s"
9 | },
10 | "nplurals=2; plural=(n != 1);");
11 |
--------------------------------------------------------------------------------
/l10n/ug.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive ئەپ سەپلىمىسى",
3 | "Error verifying OAuth2 Code for " : "ئۈچۈن OAuth2 كودىنى دەلىللەشتە خاتالىق",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "1-قەدەم مەغلۇپ بولدى. مۇستەسنا:% s",
6 | "Step 2 failed. Exception: %s" : "ئىككىنچى قەدەم مەغلۇپ بولدى. مۇستەسنا:% s"
7 | },"pluralForm" :"nplurals=2; plural=(n != 1);"
8 | }
--------------------------------------------------------------------------------
/l10n/uk.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Налаштування додатку Google Drive",
5 | "Error verifying OAuth2 Code for " : "Помилка перевірки коду OAuth2 для ",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "1-й крок невдалий. Виключення: %s",
8 | "Step 2 failed. Exception: %s" : "2-й крок невдалий. Виключення: %s"
9 | },
10 | "nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);");
11 |
--------------------------------------------------------------------------------
/l10n/uk.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Налаштування додатку Google Drive",
3 | "Error verifying OAuth2 Code for " : "Помилка перевірки коду OAuth2 для ",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "1-й крок невдалий. Виключення: %s",
6 | "Step 2 failed. Exception: %s" : "2-й крок невдалий. Виключення: %s"
7 | },"pluralForm" :"nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);"
8 | }
--------------------------------------------------------------------------------
/l10n/zh_CN.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google Drive 应用配置",
5 | "Error verifying OAuth2 Code for " : "验证 OAuth2 代码错误于",
6 | "Google Drive" : "Google Drive",
7 | "Step 1 failed. Exception: %s" : "步骤 1 失败。异常:%s",
8 | "Step 2 failed. Exception: %s" : "步骤 2 失败。异常:%s"
9 | },
10 | "nplurals=1; plural=0;");
11 |
--------------------------------------------------------------------------------
/l10n/zh_CN.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google Drive 应用配置",
3 | "Error verifying OAuth2 Code for " : "验证 OAuth2 代码错误于",
4 | "Google Drive" : "Google Drive",
5 | "Step 1 failed. Exception: %s" : "步骤 1 失败。异常:%s",
6 | "Step 2 failed. Exception: %s" : "步骤 2 失败。异常:%s"
7 | },"pluralForm" :"nplurals=1; plural=0;"
8 | }
--------------------------------------------------------------------------------
/l10n/zh_TW.js:
--------------------------------------------------------------------------------
1 | OC.L10N.register(
2 | "files_external_gdrive",
3 | {
4 | "Google Drive App Configuration" : "Google 雲端硬碟應用程式設定",
5 | "Error verifying OAuth2 Code for " : "驗證 OAuth2 代碼時發生錯誤",
6 | "Google Drive" : "Google 雲端硬碟",
7 | "Step 1 failed. Exception: %s" : "步驟 1 失敗,出現異常: %s",
8 | "Step 2 failed. Exception: %s" : "步驟 2 失敗,出現異常: %s"
9 | },
10 | "nplurals=1; plural=0;");
11 |
--------------------------------------------------------------------------------
/l10n/zh_TW.json:
--------------------------------------------------------------------------------
1 | { "translations": {
2 | "Google Drive App Configuration" : "Google 雲端硬碟應用程式設定",
3 | "Error verifying OAuth2 Code for " : "驗證 OAuth2 代碼時發生錯誤",
4 | "Google Drive" : "Google 雲端硬碟",
5 | "Step 1 failed. Exception: %s" : "步驟 1 失敗,出現異常: %s",
6 | "Step 2 failed. Exception: %s" : "步驟 2 失敗,出現異常: %s"
7 | },"pluralForm" :"nplurals=1; plural=0;"
8 | }
--------------------------------------------------------------------------------
/lib/AppInfo/Application.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * @copyright Copyright (c) 2017, ownCloud GmbH.
6 | * @license AGPL-3.0
7 | *
8 | * This code is free software: you can redistribute it and/or modify
9 | * it under the terms of the GNU Affero General Public License, version 3,
10 | * as published by the Free Software Foundation.
11 | *
12 | * This program is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License, version 3,
18 | * along with this program. If not, see
19 | *
20 | */
21 |
22 | namespace OCA\Files_external_gdrive\AppInfo;
23 |
24 | require_once __DIR__ . '/../../vendor/autoload.php';
25 |
26 | use OCP\AppFramework\App;
27 | use OCP\AppFramework\IAppContainer;
28 | use OCP\IContainer;
29 | use OCP\Files\External\Config\IBackendProvider;
30 |
31 | /**
32 | * @package OCA\Files_external_gdrive\AppInfo
33 | */
34 | class Application extends App implements IBackendProvider {
35 |
36 | public function __construct(array $urlParams = array()) {
37 | parent::__construct('files_external_gdrive', $urlParams);
38 |
39 | $container = $this->getContainer();
40 |
41 | $backendService = $container->getServer()->getStoragesBackendService();
42 | $backendService->registerBackendProvider($this);
43 | }
44 |
45 | /**
46 | * @{inheritdoc}
47 | */
48 | public function getBackends() {
49 | $container = $this->getContainer();
50 |
51 | $backends = [
52 | $container->query('OCA\Files_external_gdrive\Backend\Google'),
53 | ];
54 |
55 | return $backends;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/lib/Backend/Google.php:
--------------------------------------------------------------------------------
1 |
4 | * @author Vincent Petry
5 | *
6 | * @copyright Copyright (c) 2017, ownCloud GmbH
7 | * @license AGPL-3.0
8 | *
9 | * This code is free software: you can redistribute it and/or modify
10 | * it under the terms of the GNU Affero General Public License, version 3,
11 | * as published by the Free Software Foundation.
12 | *
13 | * This program is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU Affero General Public License for more details.
17 | *
18 | * You should have received a copy of the GNU Affero General Public License, version 3,
19 | * along with this program. If not, see
20 | *
21 | */
22 |
23 | namespace OCA\Files_external_gdrive\Backend;
24 |
25 | use OCP\IL10N;
26 | use OCP\Files\External\DefinitionParameter;
27 | use OCP\Files\External\Auth\AuthMechanism;
28 | use OCP\Files\External\Backend\Backend;
29 | use OCA\Files_External\Lib\LegacyDependencyCheckPolyfill;
30 |
31 | class Google extends Backend {
32 |
33 | use LegacyDependencyCheckPolyfill;
34 |
35 | public function __construct(IL10N $l) {
36 | $this
37 | ->setIdentifier('googledrive')
38 | ->addIdentifierAlias('\OC\Files\Storage\Google') // legacy compat
39 | ->setStorageClass('\OCA\Files_external_gdrive\Storage\Google')
40 | ->setText($l->t('Google Drive'))
41 | ->addParameters([
42 | // all parameters handled in OAuth2 mechanism
43 | ])
44 | ->addAuthScheme(AuthMechanism::SCHEME_OAUTH2)
45 | ->addCustomJs(['files_external_gdrive', 'gdrive'])
46 | ;
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/lib/Controller/OauthController.php:
--------------------------------------------------------------------------------
1 |
4 | *
5 | * @copyright Copyright (c) 2017, ownCloud GmbH
6 | * @license AGPL-3.0
7 | *
8 | * This code is free software: you can redistribute it and/or modify
9 | * it under the terms of the GNU Affero General Public License, version 3,
10 | * as published by the Free Software Foundation.
11 | *
12 | * This program is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License, version 3,
18 | * along with this program. If not, see
19 | *
20 | */
21 |
22 | namespace OCA\Files_External_Gdrive\Controller;
23 |
24 |
25 | use OCP\AppFramework\Http\DataResponse;
26 | use OCP\AppFramework\Controller;
27 | use OCP\AppFramework\Http;
28 | use OCP\IL10N;
29 | use OCP\IRequest;
30 |
31 | /**
32 | * Oauth controller for GDrive
33 | */
34 | class OauthController extends Controller {
35 | /**
36 | * L10N service
37 | *
38 | * @var IL10N
39 | */
40 | protected $l10n;
41 |
42 | /**
43 | * Creates a new storages controller.
44 | *
45 | * @param string $AppName application name
46 | * @param IRequest $request request
47 | * @param IL10N $l10n l10n service
48 | */
49 | public function __construct(
50 | $AppName,
51 | IRequest $request,
52 | IL10N $l10n
53 | ) {
54 | parent::__construct($AppName, $request);
55 | $this->l10n = $l10n;
56 | }
57 |
58 | /**
59 | * Create a storage from its parameters
60 | *
61 | * @param string $client_id
62 | * @param string $client_secret
63 | * @param string $redirect
64 | * @param int $step
65 | * @param string $code
66 | * @return IStorageConfig|DataResponse
67 | */
68 | public function receiveToken(
69 | $client_id,
70 | $client_secret,
71 | $redirect,
72 | $step,
73 | $code
74 | ) {
75 | $clientId = $client_id;
76 | $clientSecret = $client_secret;
77 | if ($clientId !== null && $clientSecret !== null && $redirect !== null) {
78 | $client = new \Google_Client();
79 | $client->setClientId((string)$clientId);
80 | $client->setClientSecret((string)$clientSecret);
81 | $client->setRedirectUri((string)$redirect);
82 | $client->setScopes(['https://www.googleapis.com/auth/drive']);
83 | $client->setApprovalPrompt('force');
84 | $client->setAccessType('offline');
85 | if ($step !== null) {
86 | $step = (int)$step;
87 | if ($step == 1) {
88 | try {
89 | $authUrl = $client->createAuthUrl();
90 | return new DataResponse(
91 | [
92 | 'status' => 'success',
93 | 'data' => ['url' => $authUrl]
94 | ]
95 | );
96 | } catch (Exception $exception) {
97 | return new DataResponse(
98 | [
99 | 'data' => [
100 | 'message' => $l->t('Step 1 failed. Exception: %s', [$exception->getMessage()])
101 | ]
102 | ],
103 | Http::STATUS_UNPROCESSABLE_ENTITY
104 | );
105 | }
106 | } else if ($step == 2 && $code !== null) {
107 | try {
108 | $token = $client->authenticate((string)$code);
109 | return new DataResponse(
110 | [
111 | 'status' => 'success',
112 | 'data' => [
113 | 'token' => (string)$token
114 | ]
115 | ]
116 | );
117 | } catch (Exception $exception) {
118 | return new DataResponse(
119 | [
120 | 'data' => [
121 | 'message' => $l->t('Step 2 failed. Exception: %s', [$exception->getMessage()])
122 | ]
123 | ],
124 | Http::STATUS_UNPROCESSABLE_ENTITY
125 | );
126 | }
127 | }
128 | }
129 | }
130 | return new DataResponse(
131 | [],
132 | Http::STATUS_BAD_REQUEST
133 | );
134 | }
135 | }
136 |
137 |
--------------------------------------------------------------------------------
/lib/Storage/Google.php:
--------------------------------------------------------------------------------
1 |
4 | * @author Arthur Schiwon
5 | * @author Bart Visscher
6 | * @author Christopher Schäpers
7 | * @author Francesco Rovelli
8 | * @author Jörn Friedrich Dreyer
9 | * @author Lukas Reschke
10 | * @author Michael Gapczynski
11 | * @author Morris Jobke
12 | * @author Philipp Kapfer
13 | * @author Robin Appelman
14 | * @author Robin McCorkell
15 | * @author Thomas Müller
16 | * @author Vincent Petry
17 | *
18 | * @copyright Copyright (c) 2017, ownCloud GmbH
19 | * @license AGPL-3.0
20 | *
21 | * This code is free software: you can redistribute it and/or modify
22 | * it under the terms of the GNU Affero General Public License, version 3,
23 | * as published by the Free Software Foundation.
24 | *
25 | * This program is distributed in the hope that it will be useful,
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | * GNU Affero General Public License for more details.
29 | *
30 | * You should have received a copy of the GNU Affero General Public License, version 3,
31 | * along with this program. If not, see
32 | *
33 | */
34 |
35 | namespace OCA\Files_external_gdrive\Storage;
36 |
37 | use GuzzleHttp\Exception\RequestException;
38 | use Icewind\Streams\IteratorDirectory;
39 | use Icewind\Streams\RetryWrapper;
40 |
41 | class Google extends \OCP\Files\Storage\StorageAdapter {
42 |
43 | private $client;
44 | private $id;
45 | private $service;
46 | private $driveFiles;
47 |
48 | private static $tempFiles = [];
49 |
50 | // Google Doc mimetypes
51 | const FOLDER = 'application/vnd.google-apps.folder';
52 | const DOCUMENT = 'application/vnd.google-apps.document';
53 | const SPREADSHEET = 'application/vnd.google-apps.spreadsheet';
54 | const DRAWING = 'application/vnd.google-apps.drawing';
55 | const PRESENTATION = 'application/vnd.google-apps.presentation';
56 | const MAP = 'application/vnd.google-apps.map';
57 |
58 | public function __construct($params) {
59 | if (isset($params['configured']) && $params['configured'] === 'true'
60 | && isset($params['client_id']) && isset($params['client_secret'])
61 | && isset($params['token'])
62 | ) {
63 | $config = [
64 | 'retry' => [
65 | 'retries' => 5
66 | ]
67 | ];
68 | $this->client = new \Google_Client($config);
69 | $this->client->setClientId($params['client_id']);
70 | $this->client->setClientSecret($params['client_secret']);
71 | $this->client->setScopes(['https://www.googleapis.com/auth/drive']);
72 | $this->client->setAccessToken($params['token']);
73 | // note: API connection is lazy
74 | $this->service = new \Google_Service_Drive($this->client);
75 | $token = json_decode($params['token'], true);
76 | $this->id = 'google::'.substr($params['client_id'], 0, 30).$token['created'];
77 | } else {
78 | throw new \Exception('Creating Google storage failed');
79 | }
80 | }
81 |
82 | public function getId() {
83 | return $this->id;
84 | }
85 |
86 | /**
87 | * Get the Google_Service_Drive_DriveFile object for the specified path.
88 | * Returns false on failure.
89 | * @param string $path
90 | * @return \Google_Service_Drive_DriveFile|false
91 | */
92 | private function getDriveFile($path) {
93 | // Remove leading and trailing slashes
94 | $path = trim($path, '/');
95 | if ($path === '.') {
96 | $path = '';
97 | }
98 | if (isset($this->driveFiles[$path])) {
99 | return $this->driveFiles[$path];
100 | } else if ($path === '') {
101 | $root = $this->service->files->get('root');
102 | $this->driveFiles[$path] = $root;
103 | return $root;
104 | } else {
105 | // Google Drive SDK does not have methods for retrieving files by path
106 | // Instead we must find the id of the parent folder of the file
107 | $parentId = $this->getDriveFile('')->getId();
108 | $folderNames = explode('/', $path);
109 | $path = '';
110 | // Loop through each folder of this path to get to the file
111 | foreach ($folderNames as $name) {
112 | // Reconstruct path from beginning
113 | if ($path === '') {
114 | $path .= $name;
115 | } else {
116 | $path .= '/'.$name;
117 | }
118 | if (isset($this->driveFiles[$path])) {
119 | $parentId = $this->driveFiles[$path]->getId();
120 | } else {
121 | $q = "title='" . str_replace("'","\\'", $name) . "' and '" . str_replace("'","\\'", $parentId) . "' in parents and trashed = false";
122 | $result = $this->service->files->listFiles(['q' => $q])->getItems();
123 | if (!empty($result)) {
124 | // Google Drive allows files with the same name, ownCloud doesn't
125 | if (count($result) > 1) {
126 | $this->onDuplicateFileDetected($path);
127 | return false;
128 | } else {
129 | $file = current($result);
130 | $this->driveFiles[$path] = $file;
131 | $parentId = $file->getId();
132 | }
133 | } else {
134 | // Google Docs have no extension in their title, so try without extension
135 | $pos = strrpos($path, '.');
136 | if ($pos !== false) {
137 | $pathWithoutExt = substr($path, 0, $pos);
138 | $file = $this->getDriveFile($pathWithoutExt);
139 | if ($file && $this->isGoogleDocFile($file)) {
140 | // Switch cached Google_Service_Drive_DriveFile to the correct index
141 | unset($this->driveFiles[$pathWithoutExt]);
142 | $this->driveFiles[$path] = $file;
143 | $parentId = $file->getId();
144 | } else {
145 | return false;
146 | }
147 | } else {
148 | return false;
149 | }
150 | }
151 | }
152 | }
153 | return $this->driveFiles[$path];
154 | }
155 | }
156 |
157 | /**
158 | * Set the Google_Service_Drive_DriveFile object in the cache
159 | * @param string $path
160 | * @param \Google_Service_Drive_DriveFile|false $file
161 | */
162 | private function setDriveFile($path, $file) {
163 | $path = trim($path, '/');
164 | $this->driveFiles[$path] = $file;
165 | if ($file === false) {
166 | // Remove all children
167 | $len = strlen($path);
168 | foreach ($this->driveFiles as $key => $file) {
169 | if (substr($key, 0, $len) === $path) {
170 | unset($this->driveFiles[$key]);
171 | }
172 | }
173 | }
174 | }
175 |
176 | /**
177 | * Write a log message to inform about duplicate file names
178 | * @param string $path
179 | */
180 | private function onDuplicateFileDetected($path) {
181 | $about = $this->service->about->get();
182 | $user = $about->getName();
183 | \OCP\Util::writeLog('files_external',
184 | 'Ignoring duplicate file name: '.$path.' on Google Drive for Google user: '.$user,
185 | \OCP\Util::INFO
186 | );
187 | }
188 |
189 | /**
190 | * Generate file extension for a Google Doc, choosing Open Document formats for download
191 | * @param string $mimetype
192 | * @return string
193 | */
194 | private function getGoogleDocExtension($mimetype) {
195 | if ($mimetype === self::DOCUMENT) {
196 | return 'odt';
197 | } else if ($mimetype === self::SPREADSHEET) {
198 | return 'ods';
199 | } else if ($mimetype === self::DRAWING) {
200 | return 'jpg';
201 | } else if ($mimetype === self::PRESENTATION) {
202 | // Download as .odp is not available
203 | return 'pdf';
204 | } else {
205 | return '';
206 | }
207 | }
208 |
209 | /**
210 | * Returns whether the given drive file is a Google Doc file
211 | *
212 | * @param \Google_Service_Drive_DriveFile
213 | *
214 | * @return true if the file is a Google Doc file, false otherwise
215 | */
216 | private function isGoogleDocFile($file) {
217 | return $this->getGoogleDocExtension($file->getMimeType()) !== '';
218 | }
219 |
220 | public function mkdir($path) {
221 | if (!$this->is_dir($path)) {
222 | $parentFolder = $this->getDriveFile(dirname($path));
223 | if ($parentFolder) {
224 | $folder = new \Google_Service_Drive_DriveFile();
225 | $folder->setTitle(basename($path));
226 | $folder->setMimeType(self::FOLDER);
227 | $parent = new \Google_Service_Drive_ParentReference();
228 | $parent->setId($parentFolder->getId());
229 | $folder->setParents([$parent]);
230 | $result = $this->service->files->insert($folder);
231 | if ($result) {
232 | $this->setDriveFile($path, $result);
233 | }
234 | return (bool)$result;
235 | }
236 | }
237 | return false;
238 | }
239 |
240 | public function rmdir($path) {
241 | if (!$this->isDeletable($path)) {
242 | return false;
243 | }
244 | if (trim($path, '/') === '') {
245 | $dir = $this->opendir($path);
246 | if(is_resource($dir)) {
247 | while (($file = readdir($dir)) !== false) {
248 | if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
249 | if (!$this->unlink($path.'/'.$file)) {
250 | return false;
251 | }
252 | }
253 | }
254 | closedir($dir);
255 | }
256 | $this->driveFiles = [];
257 | return true;
258 | } else {
259 | return $this->unlink($path);
260 | }
261 | }
262 |
263 | public function opendir($path) {
264 | $folder = $this->getDriveFile($path);
265 | if ($folder) {
266 | $files = [];
267 | $duplicates = [];
268 | $pageToken = true;
269 | while ($pageToken) {
270 | $params = [];
271 | if ($pageToken !== true) {
272 | $params['pageToken'] = $pageToken;
273 | }
274 | $params['q'] = "'" . str_replace("'","\\'", $folder->getId()) . "' in parents and trashed = false";
275 | $children = $this->service->files->listFiles($params);
276 | foreach ($children->getItems() as $child) {
277 | $name = $child->getTitle();
278 | // Check if this is a Google Doc i.e. no extension in name
279 | $extension = $child->getFileExtension();
280 | if (empty($extension)) {
281 | if ($child->getMimeType() === self::MAP) {
282 | continue; // No method known to transfer map files, ignore it
283 | } else if ($child->getMimeType() !== self::FOLDER) {
284 | $name .= '.'.$this->getGoogleDocExtension($child->getMimeType());
285 | }
286 | }
287 | if ($path === '') {
288 | $filepath = $name;
289 | } else {
290 | $filepath = $path.'/'.$name;
291 | }
292 | // Google Drive allows files with the same name, ownCloud doesn't
293 | // Prevent opendir() from returning any duplicate files
294 | $key = array_search($name, $files);
295 | if ($key !== false || isset($duplicates[$filepath])) {
296 | if (!isset($duplicates[$filepath])) {
297 | $duplicates[$filepath] = true;
298 | $this->setDriveFile($filepath, false);
299 | unset($files[$key]);
300 | $this->onDuplicateFileDetected($filepath);
301 | }
302 | } else {
303 | // Cache the Google_Service_Drive_DriveFile for future use
304 | $this->setDriveFile($filepath, $child);
305 | $files[] = $name;
306 | }
307 | }
308 | $pageToken = $children->getNextPageToken();
309 | }
310 | return IteratorDirectory::wrap($files);
311 | } else {
312 | return false;
313 | }
314 | }
315 |
316 | public function stat($path) {
317 | $file = $this->getDriveFile($path);
318 | if ($file) {
319 | $stat = [];
320 | if ($this->filetype($path) === 'dir') {
321 | $stat['size'] = 0;
322 | } else {
323 | // Check if this is a Google Doc
324 | if ($this->isGoogleDocFile($file)) {
325 | // Return unknown file size
326 | $stat['size'] = \OCP\Files\FileInfo::SPACE_UNKNOWN;
327 | } else {
328 | $stat['size'] = $file->getFileSize();
329 | }
330 | }
331 | $stat['atime'] = strtotime($file->getLastViewedByMeDate());
332 | $stat['mtime'] = strtotime($file->getModifiedDate());
333 | $stat['ctime'] = strtotime($file->getCreatedDate());
334 | return $stat;
335 | } else {
336 | return false;
337 | }
338 | }
339 |
340 | public function filetype($path) {
341 | if ($path === '') {
342 | return 'dir';
343 | } else {
344 | $file = $this->getDriveFile($path);
345 | if ($file) {
346 | if ($file->getMimeType() === self::FOLDER) {
347 | return 'dir';
348 | } else {
349 | return 'file';
350 | }
351 | } else {
352 | return false;
353 | }
354 | }
355 | }
356 |
357 | public function isUpdatable($path) {
358 | $file = $this->getDriveFile($path);
359 | if ($file) {
360 | return $file->getEditable();
361 | } else {
362 | return false;
363 | }
364 | }
365 |
366 | public function file_exists($path) {
367 | return (bool)$this->getDriveFile($path);
368 | }
369 |
370 | public function unlink($path) {
371 | $file = $this->getDriveFile($path);
372 | if ($file) {
373 | $result = $this->service->files->trash($file->getId());
374 | if ($result) {
375 | $this->setDriveFile($path, false);
376 | }
377 | return (bool)$result;
378 | } else {
379 | return false;
380 | }
381 | }
382 |
383 | public function rename($path1, $path2) {
384 | $file = $this->getDriveFile($path1);
385 | if ($file) {
386 | $newFile = $this->getDriveFile($path2);
387 | if (dirname($path1) === dirname($path2)) {
388 | if ($newFile) {
389 | // rename to the name of the target file, could be an office file without extension
390 | $file->setTitle($newFile->getTitle());
391 | } else {
392 | $file->setTitle(basename(($path2)));
393 | }
394 | } else {
395 | // Change file parent
396 | $parentFolder2 = $this->getDriveFile(dirname($path2));
397 | if ($parentFolder2) {
398 | $parent = new \Google_Service_Drive_ParentReference();
399 | $parent->setId($parentFolder2->getId());
400 | $file->setParents([$parent]);
401 | } else {
402 | return false;
403 | }
404 | }
405 | // We need to get the object for the existing file with the same
406 | // name (if there is one) before we do the patch. If oldfile
407 | // exists and is a directory we have to delete it before we
408 | // do the rename too.
409 | $oldfile = $this->getDriveFile($path2);
410 | if ($oldfile && $this->is_dir($path2)) {
411 | $this->rmdir($path2);
412 | $oldfile = false;
413 | }
414 | $result = $this->service->files->patch($file->getId(), $file);
415 | if ($result) {
416 | $this->setDriveFile($path1, false);
417 | $this->setDriveFile($path2, $result);
418 | if ($oldfile && $newFile) {
419 | // only delete if they have a different id (same id can happen for part files)
420 | if ($newFile->getId() !== $oldfile->getId()) {
421 | $this->service->files->delete($oldfile->getId());
422 | }
423 | }
424 | }
425 | return (bool)$result;
426 | } else {
427 | return false;
428 | }
429 | }
430 |
431 | public function fopen($path, $mode) {
432 | $pos = strrpos($path, '.');
433 | if ($pos !== false) {
434 | $ext = substr($path, $pos);
435 | } else {
436 | $ext = '';
437 | }
438 | switch ($mode) {
439 | case 'r':
440 | case 'rb':
441 | $file = $this->getDriveFile($path);
442 | if ($file) {
443 | $exportLinks = $file->getExportLinks();
444 | $mimetype = $this->getMimeType($path);
445 | $downloadUrl = null;
446 | if ($exportLinks && isset($exportLinks[$mimetype])) {
447 | $downloadUrl = $exportLinks[$mimetype];
448 | } else {
449 | $downloadUrl = $file->getDownloadUrl();
450 | }
451 | if (isset($downloadUrl)) {
452 | $request = new \Google_Http_Request($downloadUrl, 'GET', null, null);
453 | $httpRequest = $this->client->getAuth()->sign($request);
454 | // the library's service doesn't support streaming, so we use Guzzle instead
455 | $client = \OC::$server->getHTTPClientService()->newClient();
456 | try {
457 | $response = $client->get($downloadUrl, [
458 | 'headers' => $httpRequest->getRequestHeaders(),
459 | 'stream' => true,
460 | 'verify' => realpath(__DIR__ . '/../../vendor/google/apiclient/src/Google/IO/cacerts.pem'),
461 | ]);
462 | } catch (RequestException $e) {
463 | if(!is_null($e->getResponse())) {
464 | if ($e->getResponse()->getStatusCode() === 404) {
465 | return false;
466 | } else {
467 | throw $e;
468 | }
469 | } else {
470 | throw $e;
471 | }
472 | }
473 |
474 | $handle = $response->getBody();
475 | return RetryWrapper::wrap($handle);
476 | }
477 | }
478 | return false;
479 | case 'w':
480 | case 'wb':
481 | case 'a':
482 | case 'ab':
483 | case 'r+':
484 | case 'w+':
485 | case 'wb+':
486 | case 'a+':
487 | case 'x':
488 | case 'x+':
489 | case 'c':
490 | case 'c+':
491 | $tmpFile = \OCP\Files::tmpFile($ext);
492 | \OC\Files\Stream\Close::registerCallback($tmpFile, [$this, 'writeBack']);
493 | if ($this->file_exists($path)) {
494 | $source = $this->fopen($path, 'rb');
495 | file_put_contents($tmpFile, $source);
496 | }
497 | self::$tempFiles[$tmpFile] = $path;
498 | return fopen('close://'.$tmpFile, $mode);
499 | }
500 | }
501 |
502 | public function writeBack($tmpFile) {
503 | if (isset(self::$tempFiles[$tmpFile])) {
504 | $path = self::$tempFiles[$tmpFile];
505 | $parentFolder = $this->getDriveFile(dirname($path));
506 | if ($parentFolder) {
507 | $mimetype = \OC::$server->getMimeTypeDetector()->detect($tmpFile);
508 | $params = [
509 | 'mimeType' => $mimetype,
510 | 'uploadType' => 'media'
511 | ];
512 | $result = false;
513 |
514 | $chunkSizeBytes = 10 * 1024 * 1024;
515 |
516 | $useChunking = false;
517 | $size = filesize($tmpFile);
518 | if ($size > $chunkSizeBytes) {
519 | $useChunking = true;
520 | } else {
521 | $params['data'] = file_get_contents($tmpFile);
522 | }
523 |
524 | if ($this->file_exists($path)) {
525 | $file = $this->getDriveFile($path);
526 | $this->client->setDefer($useChunking);
527 | $request = $this->service->files->update($file->getId(), $file, $params);
528 | } else {
529 | $file = new \Google_Service_Drive_DriveFile();
530 | $file->setTitle(basename($path));
531 | $file->setMimeType($mimetype);
532 | $parent = new \Google_Service_Drive_ParentReference();
533 | $parent->setId($parentFolder->getId());
534 | $file->setParents([$parent]);
535 | $this->client->setDefer($useChunking);
536 | $request = $this->service->files->insert($file, $params);
537 | }
538 |
539 | if ($useChunking) {
540 | // Create a media file upload to represent our upload process.
541 | $media = new \Google_Http_MediaFileUpload(
542 | $this->client,
543 | $request,
544 | 'text/plain',
545 | null,
546 | true,
547 | $chunkSizeBytes
548 | );
549 | $media->setFileSize($size);
550 |
551 | // Upload the various chunks. $status will be false until the process is
552 | // complete.
553 | $status = false;
554 | $handle = fopen($tmpFile, 'rb');
555 | while (!$status && !feof($handle)) {
556 | $chunk = fread($handle, $chunkSizeBytes);
557 | $status = $media->nextChunk($chunk);
558 | }
559 |
560 | // The final value of $status will be the data from the API for the object
561 | // that has been uploaded.
562 | $result = false;
563 | if ($status !== false) {
564 | $result = $status;
565 | }
566 |
567 | fclose($handle);
568 | } else {
569 | $result = $request;
570 | }
571 |
572 | // Reset to the client to execute requests immediately in the future.
573 | $this->client->setDefer(false);
574 |
575 | if ($result) {
576 | $this->setDriveFile($path, $result);
577 | }
578 | }
579 | unlink($tmpFile);
580 | }
581 | }
582 |
583 | public function getMimeType($path) {
584 | $file = $this->getDriveFile($path);
585 | if ($file) {
586 | $mimetype = $file->getMimeType();
587 | // Convert Google Doc mimetypes, choosing Open Document formats for download
588 | if ($mimetype === self::FOLDER) {
589 | return 'httpd/unix-directory';
590 | } else if ($mimetype === self::DOCUMENT) {
591 | return 'application/vnd.oasis.opendocument.text';
592 | } else if ($mimetype === self::SPREADSHEET) {
593 | return 'application/x-vnd.oasis.opendocument.spreadsheet';
594 | } else if ($mimetype === self::DRAWING) {
595 | return 'image/jpeg';
596 | } else if ($mimetype === self::PRESENTATION) {
597 | // Download as .odp is not available
598 | return 'application/pdf';
599 | } else {
600 | // use extension-based detection, could be an encrypted file
601 | return parent::getMimeType($path);
602 | }
603 | } else {
604 | return false;
605 | }
606 | }
607 |
608 | public function free_space($path) {
609 | $about = $this->service->about->get();
610 | return $about->getQuotaBytesTotal() - $about->getQuotaBytesUsed();
611 | }
612 |
613 | public function touch($path, $mtime = null) {
614 | $file = $this->getDriveFile($path);
615 | $result = false;
616 | if ($file) {
617 | if (isset($mtime)) {
618 | // This is just RFC3339, but frustratingly, GDrive's API *requires*
619 | // the fractions portion be present, while no handy PHP constant
620 | // for RFC3339 or ISO8601 includes it. So we do it ourselves.
621 | $file->setModifiedDate(date('Y-m-d\TH:i:s.uP', $mtime));
622 | $result = $this->service->files->patch($file->getId(), $file, [
623 | 'setModifiedDate' => true,
624 | ]);
625 | } else {
626 | $result = $this->service->files->touch($file->getId());
627 | }
628 | } else {
629 | $parentFolder = $this->getDriveFile(dirname($path));
630 | if ($parentFolder) {
631 | $file = new \Google_Service_Drive_DriveFile();
632 | $file->setTitle(basename($path));
633 | $parent = new \Google_Service_Drive_ParentReference();
634 | $parent->setId($parentFolder->getId());
635 | $file->setParents([$parent]);
636 | $result = $this->service->files->insert($file);
637 | }
638 | }
639 | if ($result) {
640 | $this->setDriveFile($path, $result);
641 | }
642 | return (bool)$result;
643 | }
644 |
645 | public function test() {
646 | if ($this->free_space('')) {
647 | return true;
648 | }
649 | return false;
650 | }
651 |
652 | public function hasUpdated($path, $time) {
653 | $appConfig = \OC::$server->getAppConfig();
654 | if ($this->is_file($path)) {
655 | return parent::hasUpdated($path, $time);
656 | } else {
657 | // Google Drive doesn't change modified times of folders when files inside are updated
658 | // Instead we use the Changes API to see if folders have been updated, and it's a pain
659 | $folder = $this->getDriveFile($path);
660 | if ($folder) {
661 | $result = false;
662 | $folderId = $folder->getId();
663 | $startChangeId = $appConfig->getValue('files_external', $this->getId().'cId');
664 | $params = [
665 | 'includeDeleted' => true,
666 | 'includeSubscribed' => true,
667 | ];
668 | if (isset($startChangeId)) {
669 | $startChangeId = (int)$startChangeId;
670 | $largestChangeId = $startChangeId;
671 | $params['startChangeId'] = $startChangeId + 1;
672 | } else {
673 | $largestChangeId = 0;
674 | }
675 | $pageToken = true;
676 | while ($pageToken) {
677 | if ($pageToken !== true) {
678 | $params['pageToken'] = $pageToken;
679 | }
680 | $changes = $this->service->changes->listChanges($params);
681 | if ($largestChangeId === 0 || $largestChangeId === $startChangeId) {
682 | $largestChangeId = $changes->getLargestChangeId();
683 | }
684 | if (isset($startChangeId)) {
685 | // Check if a file in this folder has been updated
686 | // There is no way to filter by folder at the API level...
687 | foreach ($changes->getItems() as $change) {
688 | $file = $change->getFile();
689 | if ($file) {
690 | foreach ($file->getParents() as $parent) {
691 | if ($parent->getId() === $folderId) {
692 | $result = true;
693 | // Check if there are changes in different folders
694 | } else if ($change->getId() <= $largestChangeId) {
695 | // Decrement id so this change is fetched when called again
696 | $largestChangeId = $change->getId();
697 | $largestChangeId--;
698 | }
699 | }
700 | }
701 | }
702 | $pageToken = $changes->getNextPageToken();
703 | } else {
704 | // Assuming the initial scan just occurred and changes are negligible
705 | break;
706 | }
707 | }
708 | $appConfig->setValue('files_external', $this->getId().'cId', $largestChangeId);
709 | return $result;
710 | }
711 | }
712 | return false;
713 | }
714 |
715 | /**
716 | * check if curl is installed
717 | */
718 | public static function checkDependencies() {
719 | return true;
720 | }
721 |
722 | }
723 |
--------------------------------------------------------------------------------
/tests/GoogleTest.php:
--------------------------------------------------------------------------------
1 |
4 | * @author Christopher Schäpers
5 | * @author Joas Schilling
6 | * @author Michael Gapczynski
7 | * @author Morris Jobke
8 | * @author Robin Appelman
9 | * @author Robin McCorkell
10 | * @author Thomas Müller
11 | * @author Vincent Petry
12 | *
13 | * @copyright Copyright (c) 2017, ownCloud GmbH
14 | * @license AGPL-3.0
15 | *
16 | * This code is free software: you can redistribute it and/or modify
17 | * it under the terms of the GNU Affero General Public License, version 3,
18 | * as published by the Free Software Foundation.
19 | *
20 | * This program is distributed in the hope that it will be useful,
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | * GNU Affero General Public License for more details.
24 | *
25 | * You should have received a copy of the GNU Affero General Public License, version 3,
26 | * along with this program. If not, see
27 | *
28 | */
29 |
30 | namespace OCA\Files_External\Tests\Storage;
31 |
32 | use \OCA\Files_External\Lib\Storage\Google;
33 |
34 | /**
35 | * Class GoogleTest
36 | *
37 | * @group DB
38 | *
39 | * @package OCA\Files_External\Tests\Storage
40 | */
41 | class GoogleTest extends \Test\Files\Storage\Storage {
42 |
43 | private $config;
44 |
45 | protected function setUp() {
46 | parent::setUp();
47 |
48 | $this->config = include('files_external/tests/config.php');
49 | if (!is_array($this->config) || !isset($this->config['google'])
50 | || !$this->config['google']['run']
51 | ) {
52 | $this->markTestSkipped('Google Drive backend not configured');
53 | }
54 | $this->instance = new Google($this->config['google']);
55 | }
56 |
57 | protected function tearDown() {
58 | if ($this->instance) {
59 | $this->instance->rmdir('/');
60 | }
61 |
62 | parent::tearDown();
63 | }
64 |
65 | public function testSameNameAsFolderWithExtension() {
66 | $this->assertTrue($this->instance->mkdir('testsamename'));
67 | $this->assertEquals(13, $this->instance->file_put_contents('testsamename.txt', 'some contents'));
68 | $this->assertEquals('some contents', $this->instance->file_get_contents('testsamename.txt'));
69 | $this->assertTrue($this->instance->is_dir('testsamename'));
70 | $this->assertTrue($this->instance->unlink('testsamename.txt'));
71 | $this->assertTrue($this->instance->rmdir('testsamename'));
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | addPsr4('Test\\', OC::$SERVERROOT . '/tests/lib/', true);
8 | \OC::$composerAutoloader->addPsr4('Tests\\', OC::$SERVERROOT . '/tests/', true);
9 |
10 | if (!class_exists('PHPUnit_Framework_TestCase')) {
11 | require_once('PHPUnit/Autoload.php');
12 | }
13 |
14 | OC_Hook::clear();
15 |
--------------------------------------------------------------------------------
/tests/config.json.example:
--------------------------------------------------------------------------------
1 | {
2 | 'client_id' => '',
3 | 'client_secret' => '',
4 | 'token' => '',
5 | }
6 |
--------------------------------------------------------------------------------
/tests/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 | .
11 |
12 |
13 |
14 |
15 | ../../files_external_gdrive
16 |
17 | ../../files_external_gdrive/tests
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------