The GDPR Framework By Data443 - Version 1.0.0

Version Description

Download this release

Release Info

Developer indrek_k
Plugin Icon 128x128 The GDPR Framework By Data443
Version 1.0.0
Comparing to
See all releases

Version 1.0.0

Files changed (387) hide show
  1. assets/1.png +0 -0
  2. assets/2.png +0 -0
  3. assets/3.png +0 -0
  4. assets/4.png +0 -0
  5. assets/5.png +0 -0
  6. assets/conditional-show.js +102 -0
  7. assets/data-protection-authorities.php +183 -0
  8. assets/gdpr-admin.css +362 -0
  9. assets/gdpr-admin.js +102 -0
  10. assets/gdpr-badge.svg +1 -0
  11. assets/gdpr-installer.css +550 -0
  12. assets/gdpr-installer.js +58 -0
  13. assets/gdpr-rhino.svg +42 -0
  14. assets/icon-sortable.png +0 -0
  15. assets/jquery.repeater.min.js +5 -0
  16. gdpr-framework.php +119 -0
  17. license.txt +199 -0
  18. readme.txt +58 -0
  19. src/Admin/AdminError.php +17 -0
  20. src/Admin/AdminHelper.php +57 -0
  21. src/Admin/AdminNotice.php +41 -0
  22. src/Admin/AdminTab.php +157 -0
  23. src/Admin/AdminTabGeneral.php +254 -0
  24. src/Admin/AdminTabInterface.php +40 -0
  25. src/Admin/Modal.php +41 -0
  26. src/Admin/WordpressAdmin.php +156 -0
  27. src/Admin/WordpressAdminPage.php +147 -0
  28. src/Components/Consent/AdminTabConsent.php +181 -0
  29. src/Components/Consent/ConsentAdmin.php +20 -0
  30. src/Components/Consent/ConsentManager.php +317 -0
  31. src/Components/Consent/UserConsentModel.php +282 -0
  32. src/Components/PrivacyPolicy/AdminTabPrivacyPolicy.php +471 -0
  33. src/Components/PrivacyPolicy/PolicyGenerator.php +45 -0
  34. src/Components/PrivacyPolicy/PrivacyPolicy.php +92 -0
  35. src/Components/PrivacyToolsPage/PrivacyToolsPage.php +15 -0
  36. src/Components/PrivacyToolsPage/PrivacyToolsPageController.php +261 -0
  37. src/Components/PrivacyToolsPage/PrivacyToolsPageShortcode.php +45 -0
  38. src/Components/Support/AdminTabSupport.php +34 -0
  39. src/Components/Support/Support.php +18 -0
  40. src/Components/Themes/Themes.php +99 -0
  41. src/Components/WordpressComments/WordpressComments.php +173 -0
  42. src/Components/WordpressUser/Controllers/DashboardDataPageController.php +197 -0
  43. src/Components/WordpressUser/Controllers/DashboardProfilePageController.php +123 -0
  44. src/Components/WordpressUser/DataManager.php +151 -0
  45. src/Components/WordpressUser/RegistrationForm.php +50 -0
  46. src/Components/WordpressUser/WordpressUser.php +111 -0
  47. src/Config.php +8 -0
  48. src/Container.php +8 -0
  49. src/DataSubject/AdminTabDataSubject.php +126 -0
  50. src/DataSubject/DataExporter.php +138 -0
  51. src/DataSubject/DataRepository.php +191 -0
  52. src/DataSubject/DataSubject.php +175 -0
  53. src/DataSubject/DataSubjectAdmin.php +18 -0
  54. src/DataSubject/DataSubjectAuthenticator.php +125 -0
  55. src/DataSubject/DataSubjectIdentificator.php +184 -0
  56. src/DataSubject/DataSubjectManager.php +80 -0
  57. src/Database/WordpressDatabase.php +266 -0
  58. src/Helpers.php +153 -0
  59. src/Installer/AdminInstallerNotice.php +19 -0
  60. src/Installer/Installer.php +303 -0
  61. src/Installer/InstallerRouter.php +184 -0
  62. src/Installer/InstallerStep.php +200 -0
  63. src/Installer/InstallerStepInterface.php +22 -0
  64. src/Installer/InstallerWizard.php +49 -0
  65. src/Installer/Steps/ConfigurationPages.php +61 -0
  66. src/Installer/Steps/ConfigurationSettings.php +71 -0
  67. src/Installer/Steps/Consent.php +30 -0
  68. src/Installer/Steps/Disclaimer.php +24 -0
  69. src/Installer/Steps/Finish.php +27 -0
  70. src/Installer/Steps/Integrations.php +48 -0
  71. src/Installer/Steps/PolicyContents.php +31 -0
  72. src/Installer/Steps/PolicySettings.php +214 -0
  73. src/Installer/Steps/Welcome.php +29 -0
  74. src/Modules/ContactForm7/ContactForm7.php +108 -0
  75. src/Modules/WPML/WPML.php +149 -0
  76. src/Options/Options.php +66 -0
  77. src/Options/OptionsBase.php +71 -0
  78. src/Router.php +166 -0
  79. src/Setup.php +93 -0
  80. src/SetupAdmin.php +62 -0
  81. src/View.php +64 -0
  82. vendor/autoload.php +7 -0
  83. vendor/composer/ClassLoader.php +445 -0
  84. vendor/composer/LICENSE +21 -0
  85. vendor/composer/autoload_classmap.php +9 -0
  86. vendor/composer/autoload_files.php +11 -0
  87. vendor/composer/autoload_namespaces.php +9 -0
  88. vendor/composer/autoload_psr4.php +17 -0
  89. vendor/composer/autoload_real.php +70 -0
  90. vendor/composer/autoload_static.php +80 -0
  91. vendor/composer/installed.json +419 -0
  92. vendor/doctrine/inflector/LICENSE +19 -0
  93. vendor/doctrine/inflector/README.md +6 -0
  94. vendor/doctrine/inflector/composer.json +32 -0
  95. vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php +490 -0
  96. vendor/illuminate/config/Repository.php +154 -0
  97. vendor/illuminate/config/composer.json +36 -0
  98. vendor/illuminate/container/BoundMethod.php +172 -0
  99. vendor/illuminate/container/Container.php +1222 -0
  100. vendor/illuminate/container/ContextualBindingBuilder.php +68 -0
  101. vendor/illuminate/container/composer.json +34 -0
  102. vendor/illuminate/contracts/Auth/Access/Authorizable.php +15 -0
  103. vendor/illuminate/contracts/Auth/Access/Gate.php +104 -0
  104. vendor/illuminate/contracts/Auth/Authenticatable.php +49 -0
  105. vendor/illuminate/contracts/Auth/CanResetPassword.php +21 -0
  106. vendor/illuminate/contracts/Auth/Factory.php +22 -0
  107. vendor/illuminate/contracts/Auth/Guard.php +50 -0
  108. vendor/illuminate/contracts/Auth/PasswordBroker.php +76 -0
  109. vendor/illuminate/contracts/Auth/PasswordBrokerFactory.php +14 -0
  110. vendor/illuminate/contracts/Auth/StatefulGuard.php +63 -0
  111. vendor/illuminate/contracts/Auth/SupportsBasicAuth.php +24 -0
  112. vendor/illuminate/contracts/Auth/UserProvider.php +49 -0
  113. vendor/illuminate/contracts/Broadcasting/Broadcaster.php +33 -0
  114. vendor/illuminate/contracts/Broadcasting/Factory.php +14 -0
  115. vendor/illuminate/contracts/Broadcasting/ShouldBroadcast.php +13 -0
  116. vendor/illuminate/contracts/Broadcasting/ShouldBroadcastNow.php +8 -0
  117. vendor/illuminate/contracts/Bus/Dispatcher.php +31 -0
  118. vendor/illuminate/contracts/Bus/QueueingDispatcher.php +14 -0
  119. vendor/illuminate/contracts/Cache/Factory.php +14 -0
  120. vendor/illuminate/contracts/Cache/Repository.php +117 -0
  121. vendor/illuminate/contracts/Cache/Store.php +92 -0
  122. vendor/illuminate/contracts/Config/Repository.php +57 -0
  123. vendor/illuminate/contracts/Console/Application.php +22 -0
  124. vendor/illuminate/contracts/Console/Kernel.php +47 -0
  125. vendor/illuminate/contracts/Container/BindingResolutionException.php +10 -0
  126. vendor/illuminate/contracts/Container/Container.php +151 -0
  127. vendor/illuminate/contracts/Container/ContextualBindingBuilder.php +22 -0
  128. vendor/illuminate/contracts/Cookie/Factory.php +43 -0
  129. vendor/illuminate/contracts/Cookie/QueueingFactory.php +28 -0
  130. vendor/illuminate/contracts/Database/ModelIdentifier.php +35 -0
  131. vendor/illuminate/contracts/Debug/ExceptionHandler.php +34 -0
  132. vendor/illuminate/contracts/Encryption/DecryptException.php +10 -0
  133. vendor/illuminate/contracts/Encryption/EncryptException.php +10 -0
  134. vendor/illuminate/contracts/Encryption/Encrypter.php +24 -0
  135. vendor/illuminate/contracts/Events/Dispatcher.php +82 -0
  136. vendor/illuminate/contracts/Filesystem/Cloud.php +14 -0
  137. vendor/illuminate/contracts/Filesystem/Factory.php +14 -0
  138. vendor/illuminate/contracts/Filesystem/FileNotFoundException.php +10 -0
  139. vendor/illuminate/contracts/Filesystem/Filesystem.php +175 -0
  140. vendor/illuminate/contracts/Foundation/Application.php +92 -0
  141. vendor/illuminate/contracts/Hashing/Hasher.php +34 -0
  142. vendor/illuminate/contracts/Http/Kernel.php +37 -0
  143. vendor/illuminate/contracts/Logging/Log.php +98 -0
  144. vendor/illuminate/contracts/Mail/MailQueue.php +29 -0
  145. vendor/illuminate/contracts/Mail/Mailable.php +33 -0
  146. vendor/illuminate/contracts/Mail/Mailer.php +32 -0
  147. vendor/illuminate/contracts/Notifications/Dispatcher.php +24 -0
  148. vendor/illuminate/contracts/Notifications/Factory.php +32 -0
  149. vendor/illuminate/contracts/Pagination/LengthAwarePaginator.php +29 -0
  150. vendor/illuminate/contracts/Pagination/Paginator.php +110 -0
  151. vendor/illuminate/contracts/Pipeline/Hub.php +15 -0
  152. vendor/illuminate/contracts/Pipeline/Pipeline.php +40 -0
  153. vendor/illuminate/contracts/Queue/EntityNotFoundException.php +22 -0
  154. vendor/illuminate/contracts/Queue/EntityResolver.php +15 -0
  155. vendor/illuminate/contracts/Queue/Factory.php +14 -0
  156. vendor/illuminate/contracts/Queue/Job.php +108 -0
  157. vendor/illuminate/contracts/Queue/Monitor.php +30 -0
  158. vendor/illuminate/contracts/Queue/Queue.php +99 -0
  159. vendor/illuminate/contracts/Queue/QueueableCollection.php +20 -0
  160. vendor/illuminate/contracts/Queue/QueueableEntity.php +13 -0
  161. vendor/illuminate/contracts/Queue/ShouldQueue.php +8 -0
  162. vendor/illuminate/contracts/Redis/Factory.php +14 -0
  163. vendor/illuminate/contracts/Routing/BindingRegistrar.php +23 -0
  164. vendor/illuminate/contracts/Routing/Registrar.php +105 -0
  165. vendor/illuminate/contracts/Routing/ResponseFactory.php +126 -0
  166. vendor/illuminate/contracts/Routing/UrlGenerator.php +71 -0
  167. vendor/illuminate/contracts/Routing/UrlRoutable.php +20 -0
  168. vendor/illuminate/contracts/Session/Session.php +165 -0
  169. vendor/illuminate/contracts/Support/Arrayable.php +13 -0
  170. vendor/illuminate/contracts/Support/Htmlable.php +13 -0
  171. vendor/illuminate/contracts/Support/Jsonable.php +14 -0
  172. vendor/illuminate/contracts/Support/MessageBag.php +93 -0
  173. vendor/illuminate/contracts/Support/MessageProvider.php +13 -0
  174. vendor/illuminate/contracts/Support/Renderable.php +13 -0
  175. vendor/illuminate/contracts/Translation/Translator.php +42 -0
  176. vendor/illuminate/contracts/Validation/Factory.php +46 -0
  177. vendor/illuminate/contracts/Validation/ValidatesWhenResolved.php +13 -0
  178. vendor/illuminate/contracts/Validation/Validator.php +40 -0
  179. vendor/illuminate/contracts/View/Factory.php +79 -0
  180. vendor/illuminate/contracts/View/View.php +24 -0
  181. vendor/illuminate/contracts/composer.json +33 -0
  182. vendor/illuminate/filesystem/Filesystem.php +570 -0
  183. vendor/illuminate/filesystem/FilesystemAdapter.php +548 -0
  184. vendor/illuminate/filesystem/FilesystemManager.php +345 -0
  185. vendor/illuminate/filesystem/FilesystemServiceProvider.php +82 -0
  186. vendor/illuminate/filesystem/composer.json +41 -0
  187. vendor/illuminate/support/AggregateServiceProvider.php +52 -0
  188. vendor/illuminate/support/Arr.php +602 -0
  189. vendor/illuminate/support/Collection.php +1655 -0
  190. vendor/illuminate/support/Composer.php +100 -0
  191. vendor/illuminate/support/Debug/Dumper.php +26 -0
  192. vendor/illuminate/support/Debug/HtmlDumper.php +29 -0
  193. vendor/illuminate/support/Facades/App.php +31 -0
  194. vendor/illuminate/support/Facades/Artisan.php +27 -0
  195. vendor/illuminate/support/Facades/Auth.php +48 -0
  196. vendor/illuminate/support/Facades/Blade.php +19 -0
  197. vendor/illuminate/support/Facades/Broadcast.php +21 -0
  198. vendor/illuminate/support/Facades/Bus.php +32 -0
  199. vendor/illuminate/support/Facades/Cache.php +20 -0
  200. vendor/illuminate/support/Facades/Config.php +19 -0
  201. vendor/illuminate/support/Facades/Cookie.php +42 -0
  202. vendor/illuminate/support/Facades/Crypt.php +19 -0
  203. vendor/illuminate/support/Facades/DB.php +20 -0
  204. vendor/illuminate/support/Facades/Event.php +34 -0
  205. vendor/illuminate/support/Facades/Facade.php +223 -0
  206. vendor/illuminate/support/Facades/File.php +19 -0
  207. vendor/illuminate/support/Facades/Gate.php +21 -0
  208. vendor/illuminate/support/Facades/Hash.php +19 -0
  209. vendor/illuminate/support/Facades/Input.php +33 -0
  210. vendor/illuminate/support/Facades/Lang.php +19 -0
  211. vendor/illuminate/support/Facades/Log.php +21 -0
  212. vendor/illuminate/support/Facades/Mail.php +31 -0
  213. vendor/illuminate/support/Facades/Notification.php +32 -0
  214. vendor/illuminate/support/Facades/Password.php +54 -0
  215. vendor/illuminate/support/Facades/Queue.php +32 -0
  216. vendor/illuminate/support/Facades/Redirect.php +19 -0
  217. vendor/illuminate/support/Facades/Redis.php +20 -0
  218. vendor/illuminate/support/Facades/Request.php +19 -0
  219. vendor/illuminate/support/Facades/Response.php +21 -0
  220. vendor/illuminate/support/Facades/Route.php +35 -0
  221. vendor/illuminate/support/Facades/Schema.php +35 -0
  222. vendor/illuminate/support/Facades/Session.php +20 -0
  223. vendor/illuminate/support/Facades/Storage.php +50 -0
  224. vendor/illuminate/support/Facades/URL.php +19 -0
  225. vendor/illuminate/support/Facades/Validator.php +19 -0
  226. vendor/illuminate/support/Facades/View.php +19 -0
  227. vendor/illuminate/support/Fluent.php +192 -0
  228. vendor/illuminate/support/HigherOrderCollectionProxy.php +63 -0
  229. vendor/illuminate/support/HigherOrderTapProxy.php +38 -0
  230. vendor/illuminate/support/HtmlString.php +46 -0
  231. vendor/illuminate/support/Manager.php +140 -0
  232. vendor/illuminate/support/MessageBag.php +384 -0
  233. vendor/illuminate/support/NamespacedItemResolver.php +106 -0
  234. vendor/illuminate/support/Pluralizer.php +114 -0
  235. vendor/illuminate/support/ServiceProvider.php +277 -0
  236. vendor/illuminate/support/Str.php +646 -0
  237. vendor/illuminate/support/Testing/Fakes/BusFake.php +113 -0
  238. vendor/illuminate/support/Testing/Fakes/EventFake.php +197 -0
  239. vendor/illuminate/support/Testing/Fakes/MailFake.php +178 -0
  240. vendor/illuminate/support/Testing/Fakes/NotificationFake.php +165 -0
  241. vendor/illuminate/support/Testing/Fakes/PendingMailFake.php +53 -0
  242. vendor/illuminate/support/Testing/Fakes/QueueFake.php +238 -0
  243. vendor/illuminate/support/Traits/CapsuleManagerTrait.php +69 -0
  244. vendor/illuminate/support/Traits/Macroable.php +83 -0
  245. vendor/illuminate/support/ViewErrorBag.php +120 -0
  246. vendor/illuminate/support/composer.json +48 -0
  247. vendor/illuminate/support/helpers.php +1038 -0
  248. vendor/paragonie/random_compat/LICENSE +22 -0
  249. vendor/paragonie/random_compat/build-phar.sh +5 -0
  250. vendor/paragonie/random_compat/composer.json +37 -0
  251. vendor/paragonie/random_compat/dist/random_compat.phar.pubkey +5 -0
  252. vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc +11 -0
  253. vendor/paragonie/random_compat/lib/byte_safe_strings.php +181 -0
  254. vendor/paragonie/random_compat/lib/cast_to_int.php +75 -0
  255. vendor/paragonie/random_compat/lib/error_polyfill.php +49 -0
  256. vendor/paragonie/random_compat/lib/random.php +223 -0
  257. vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php +88 -0
  258. vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php +167 -0
  259. vendor/paragonie/random_compat/lib/random_bytes_libsodium.php +88 -0
  260. vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php +92 -0
  261. vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php +77 -0
  262. vendor/paragonie/random_compat/lib/random_int.php +190 -0
  263. vendor/paragonie/random_compat/other/build_phar.php +57 -0
  264. vendor/paragonie/random_compat/psalm-autoload.php +9 -0
  265. vendor/paragonie/random_compat/psalm.xml +16 -0
  266. vendor/symfony/finder/.gitignore +3 -0
  267. vendor/symfony/finder/CHANGELOG.md +55 -0
  268. vendor/symfony/finder/Comparator/Comparator.php +98 -0
  269. vendor/symfony/finder/Comparator/DateComparator.php +51 -0
  270. vendor/symfony/finder/Comparator/NumberComparator.php +79 -0
  271. vendor/symfony/finder/Exception/AccessDeniedException.php +19 -0
  272. vendor/symfony/finder/Exception/ExceptionInterface.php +25 -0
  273. vendor/symfony/finder/Finder.php +731 -0
  274. vendor/symfony/finder/Glob.php +116 -0
  275. vendor/symfony/finder/Iterator/CustomFilterIterator.php +61 -0
  276. vendor/symfony/finder/Iterator/DateRangeFilterIterator.php +58 -0
  277. vendor/symfony/finder/Iterator/DepthRangeFilterIterator.php +45 -0
  278. vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php +84 -0
  279. vendor/symfony/finder/Iterator/FileTypeFilterIterator.php +53 -0
  280. vendor/symfony/finder/Iterator/FilecontentFilterIterator.php +58 -0
  281. vendor/symfony/finder/Iterator/FilenameFilterIterator.php +47 -0
  282. vendor/symfony/finder/Iterator/FilterIterator.php +60 -0
  283. vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php +112 -0
  284. vendor/symfony/finder/Iterator/PathFilterIterator.php +56 -0
  285. vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php +154 -0
  286. vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php +57 -0
  287. vendor/symfony/finder/Iterator/SortableIterator.php +80 -0
  288. vendor/symfony/finder/LICENSE +19 -0
  289. vendor/symfony/finder/README.md +14 -0
  290. vendor/symfony/finder/SplFileInfo.php +79 -0
  291. vendor/symfony/finder/Tests/Comparator/ComparatorTest.php +65 -0
  292. vendor/symfony/finder/Tests/Comparator/DateComparatorTest.php +64 -0
  293. vendor/symfony/finder/Tests/Comparator/NumberComparatorTest.php +108 -0
  294. vendor/symfony/finder/Tests/FinderTest.php +698 -0
  295. vendor/symfony/finder/Tests/Fixtures/.dot/a +0 -0
  296. vendor/symfony/finder/Tests/Fixtures/.dot/b/c.neon +0 -0
  297. vendor/symfony/finder/Tests/Fixtures/.dot/b/d.neon +0 -0
  298. vendor/symfony/finder/Tests/Fixtures/A/B/C/abc.dat +0 -0
  299. vendor/symfony/finder/Tests/Fixtures/A/B/ab.dat +0 -0
  300. vendor/symfony/finder/Tests/Fixtures/A/a.dat +0 -0
  301. vendor/symfony/finder/Tests/Fixtures/copy/A/B/C/abc.dat.copy +0 -0
  302. vendor/symfony/finder/Tests/Fixtures/copy/A/B/ab.dat.copy +0 -0
  303. vendor/symfony/finder/Tests/Fixtures/copy/A/a.dat.copy +0 -0
  304. vendor/symfony/finder/Tests/Fixtures/dolor.txt +2 -0
  305. vendor/symfony/finder/Tests/Fixtures/ipsum.txt +2 -0
  306. vendor/symfony/finder/Tests/Fixtures/lorem.txt +2 -0
  307. vendor/symfony/finder/Tests/Fixtures/one/.dot +1 -0
  308. vendor/symfony/finder/Tests/Fixtures/one/a +0 -0
  309. vendor/symfony/finder/Tests/Fixtures/one/b/c.neon +0 -0
  310. vendor/symfony/finder/Tests/Fixtures/one/b/d.neon +0 -0
  311. vendor/symfony/finder/Tests/Fixtures/r+e.gex[c]a(r)s/dir/bar.dat +0 -0
  312. vendor/symfony/finder/Tests/Fixtures/with space/foo.txt +0 -0
  313. vendor/symfony/finder/Tests/GlobTest.php +95 -0
  314. vendor/symfony/finder/Tests/Iterator/CustomFilterIteratorTest.php +46 -0
  315. vendor/symfony/finder/Tests/Iterator/DateRangeFilterIteratorTest.php +74 -0
  316. vendor/symfony/finder/Tests/Iterator/DepthRangeFilterIteratorTest.php +83 -0
  317. vendor/symfony/finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php +80 -0
  318. vendor/symfony/finder/Tests/Iterator/FileTypeFilterIteratorTest.php +73 -0
  319. vendor/symfony/finder/Tests/Iterator/FilecontentFilterIteratorTest.php +86 -0
  320. vendor/symfony/finder/Tests/Iterator/FilenameFilterIteratorTest.php +54 -0
  321. vendor/symfony/finder/Tests/Iterator/FilterIteratorTest.php +53 -0
  322. vendor/symfony/finder/Tests/Iterator/Iterator.php +55 -0
  323. vendor/symfony/finder/Tests/Iterator/IteratorTestCase.php +100 -0
  324. vendor/symfony/finder/Tests/Iterator/MockFileListIterator.php +21 -0
  325. vendor/symfony/finder/Tests/Iterator/MockSplFileInfo.php +132 -0
  326. vendor/symfony/finder/Tests/Iterator/MultiplePcreFilterIteratorTest.php +71 -0
  327. vendor/symfony/finder/Tests/Iterator/PathFilterIteratorTest.php +82 -0
  328. vendor/symfony/finder/Tests/Iterator/RealIteratorTestCase.php +110 -0
  329. vendor/symfony/finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php +59 -0
  330. vendor/symfony/finder/Tests/Iterator/SizeRangeFilterIteratorTest.php +69 -0
  331. vendor/symfony/finder/Tests/Iterator/SortableIteratorTest.php +183 -0
  332. vendor/symfony/finder/composer.json +33 -0
  333. vendor/symfony/finder/phpunit.xml.dist +30 -0
  334. views/admin/consent.php +105 -0
  335. views/admin/data-subjects/search-form.php +18 -0
  336. views/admin/data-subjects/search-results.php +33 -0
  337. views/admin/general/delete-action-email.php +7 -0
  338. views/admin/general/delete-action-reassign.php +11 -0
  339. views/admin/general/delete-action.php +3 -0
  340. views/admin/general/description-data-page.php +3 -0
  341. views/admin/general/description-delete-action.php +3 -0
  342. views/admin/general/description-export-action.php +3 -0
  343. views/admin/general/description-terms-page.php +3 -0
  344. views/admin/general/enable.php +13 -0
  345. views/admin/general/export-action-email.php +7 -0
  346. views/admin/general/export-action.php +3 -0
  347. views/admin/general/theme-compatibility.php +10 -0
  348. views/admin/modals/footer.php +6 -0
  349. views/admin/modals/header.php +5 -0
  350. views/admin/modals/test.php +8 -0
  351. views/admin/notice.php +3 -0
  352. views/admin/notices/disclaimer.php +16 -0
  353. views/admin/notices/error.php +3 -0
  354. views/admin/notices/footer-step.php +3 -0
  355. views/admin/notices/footer.php +2 -0
  356. views/admin/notices/header-step.php +5 -0
  357. views/admin/notices/header.php +4 -0
  358. views/admin/notices/help.php +3 -0
  359. views/admin/notices/helper-autoinstall.php +9 -0
  360. views/admin/notices/helper-policy.php +9 -0
  361. views/admin/notices/helper-tools.php +9 -0
  362. views/admin/notices/installer-finished.php +1 -0
  363. views/admin/privacy-policy/company-location.php +3 -0
  364. views/admin/privacy-policy/description-policy-page.php +3 -0
  365. views/admin/privacy-policy/dpa.php +3 -0
  366. views/admin/privacy-policy/generated.php +20 -0
  367. views/admin/privacy-policy/has-dpo.php +12 -0
  368. views/admin/privacy-policy/header.php +3 -0
  369. views/admin/settings-page.php +38 -0
  370. views/admin/support/contents.php +62 -0
  371. views/admin/wizard-buttons.php +5 -0
  372. views/email/action-export.php +9 -0
  373. views/email/action-forget.php +13 -0
  374. views/email/identify-data-subject.php +11 -0
  375. views/email/no-data.php +6 -0
  376. views/email/request-export.php +14 -0
  377. views/email/request-forget.php +14 -0
  378. views/global/country-options.php +23 -0
  379. views/global/delete-action.php +20 -0
  380. views/global/export-action.php +14 -0
  381. views/global/html-data.php +39 -0
  382. views/installer/continue-notice.php +10 -0
  383. views/installer/footer.php +41 -0
  384. views/installer/header.php +64 -0
  385. views/installer/nonce.php +1 -0
  386. views/installer/steps/configuration-pages.php +37 -0
  387. views/installer/steps/configuration-settings.php +48 -0
assets/1.png ADDED
Binary file
assets/2.png ADDED
Binary file
assets/3.png ADDED
Binary file
assets/4.png ADDED
Binary file
assets/5.png ADDED
Binary file
assets/conditional-show.js ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function ($) {
2
+
3
+ var trigger = function () {
4
+ $('.js-gdpr-conditional').each(function () {
5
+ conditionalShow($(this));
6
+ });
7
+ };
8
+
9
+ var conditionalShow = function ($el) {
10
+ var type = $el.prop('tagName');
11
+ if ('SELECT' === type) {
12
+ conditionalShowSelect($el);
13
+ } else if ('INPUT' === type) {
14
+ if ('checkbox' === $el.attr('type')) {
15
+ conditionalShowCheckbox($el);
16
+ } else if ('radio' === $el.attr('type')) {
17
+ conditionalShowRadio($el);
18
+ } else {
19
+ console.log('Unknown element type: ' + type);
20
+ }
21
+ } else {
22
+ console.log('Unknown element type: ' + type);
23
+ }
24
+ };
25
+
26
+ var conditionalShowSelect = function ($el) {
27
+ $targets = [];
28
+ $el.find('option').each(function () {
29
+ if ($(this).data('show')) {
30
+ $targets.push($(this).data('show'));
31
+ }
32
+ });
33
+
34
+ $.each($targets, function (i, e) {
35
+ $(e).hide();
36
+ });
37
+
38
+ if ($el.is(':visible')) {
39
+ $el.find('option:selected').each(function () {
40
+ if ($(this).data('show')) {
41
+ $($(this).data('show')).show();
42
+ }
43
+ });
44
+ }
45
+ }
46
+
47
+ var conditionalShowCheckbox = function ($el) {
48
+ if ($el.is(':checked') && $el.is(':visible')) {
49
+ if ($el.data('show')) {
50
+ if (isChange) {
51
+ $($el.data('show')).addClass('slidePadding').slideDown();
52
+ } else {
53
+ $($el.data('show')).show();
54
+ }
55
+ }
56
+ } else {
57
+ if ($el.data('show')) {
58
+ if (isChange) {
59
+ $($el.data('show')).addClass('slidePadding').slideUp();
60
+ } else {
61
+ $($el.data('show')).hide();
62
+ }
63
+ }
64
+ }
65
+ };
66
+
67
+ var conditionalShowRadio = function ($el) {
68
+ $el.closest('fieldset').find('input[type=radio]').each(function (i, el) {
69
+ if ($(el).is(':checked') && $el.is(':visible')) {
70
+ if ($(el).data('show')) {
71
+ if (isChange) {
72
+ $($(el).data('show')).addClass('slidePadding').slideDown();
73
+ } else {
74
+ $($(el).data('show')).show();
75
+ }
76
+ }
77
+ } else {
78
+ if ($(el).data('show')) {
79
+ if (isChange) {
80
+ $($(el).data('show')).addClass('slidePadding').slideUp();
81
+ } else {
82
+ $($(el).data('show')).hide();
83
+ }
84
+ }
85
+ }
86
+ });
87
+ };
88
+
89
+ var isChange = false;
90
+
91
+ $('.js-gdpr-conditional').each(function () {
92
+ $(this).on('change', function () {
93
+ isChange = true;
94
+ conditionalShow($(this));
95
+
96
+ // Hacky solution for 2nd layer of nested items
97
+ trigger();
98
+ });
99
+ conditionalShow($(this));
100
+ });
101
+
102
+ });
assets/data-protection-authorities.php ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php return [
2
+ 'AT' => [
3
+ 'phone' => '+43 1 531 15 202525',
4
+ 'email' => 'dsb@dsb.gv.at',
5
+ 'website' => 'http://www.dsb.gv.at/',
6
+ ],
7
+ 'BE' => [
8
+ 'phone' => '+32 2 274 48 00',
9
+ 'email' => 'commission@privacycommission.be',
10
+ 'website' => 'http://www.privacycommission.be/',
11
+ ],
12
+ 'BG' => [
13
+ 'phone' => '+359 2 915 3580',
14
+ 'email' => 'kzld@cpdp.bg',
15
+ 'website' => 'http://www.cpdp.bg/',
16
+ ],
17
+ 'HR' => [
18
+ 'phone' => '+385 1 4609 000',
19
+ 'email' => 'info@azop.hr',
20
+ 'website' => 'http://www.azop.hr/',
21
+ ],
22
+ 'CY' => [
23
+ 'phone' => '+357 22 818 456',
24
+ 'email' => 'commissioner@dataprotection.gov.cy',
25
+ 'website' => 'http://www.dataprotection.gov.cy/',
26
+ ],
27
+ 'CZ' => [
28
+ 'phone' => '+420 234 665 111',
29
+ 'email' => 'posta@uoou.cz',
30
+ 'website' => 'http://www.uoou.cz/',
31
+ ],
32
+ 'DK' => [
33
+ 'phone' => '+45 33 1932 00',
34
+ 'email' => 'dt@datatilsynet.dk',
35
+ 'website' => 'http://www.datatilsynet.dk/',
36
+ ],
37
+ 'EE' => [
38
+ 'phone' => '+372 6274 135',
39
+ 'email' => 'info@aki.ee',
40
+ 'website' => 'http://www.aki.ee/en',
41
+ ],
42
+ 'FI' => [
43
+ 'phone' => '+358 10 3666 700',
44
+ 'email' => 'tietosuoja@om.fi',
45
+ 'website' => 'http://www.tietosuoja.fi/en/',
46
+ ],
47
+ 'FR' => [
48
+ 'phone' => ' +33 1 53 73 22 22',
49
+ 'email' => '',
50
+ 'website' => 'http://www.cnil.fr/',
51
+ ],
52
+ 'DE' => [
53
+ 'phone' => '+49 228 997799 0',
54
+ 'email' => 'poststelle@bfdi.bund.de',
55
+ 'website' => 'http://www.bfdi.bund.de/',
56
+ ],
57
+ 'GR' => [
58
+ 'phone' => '+30 210 6475 600',
59
+ 'email' => 'contact@dpa.gr',
60
+ 'website' => 'http://www.dpa.gr/',
61
+ ],
62
+ 'HU' => [
63
+ 'phone' => '+36 1 3911 400',
64
+ 'email' => 'peterfalvi.attila@naih.hu',
65
+ 'website' => 'http://www.naih.hu/',
66
+ ],
67
+ 'IE' => [
68
+ 'phone' => '+353 57 868 4800',
69
+ 'email' => 'info@dataprotection.ie',
70
+ 'website' => 'https://www.dataprotection.ie/',
71
+ ],
72
+ 'IT' => [
73
+ 'phone' => '+39 06 69677 1',
74
+ 'email' => 'garante@garanteprivacy.it',
75
+ 'website' => 'http://www.garanteprivacy.it/',
76
+ ],
77
+ 'LV' => [
78
+ 'phone' => '+371 6722 3131',
79
+ 'email' => 'info@dvi.gov.lv',
80
+ 'website' => 'http://www.dvi.gov.lv/',
81
+ ],
82
+ 'LT' => [
83
+ 'phone' => '+370 5 279 14 45 ',
84
+ 'email' => 'ada@ada.lt',
85
+ 'website' => 'http://www.ada.lt/',
86
+ ],
87
+ 'LU' => [
88
+ 'phone' => '+352 2610 60 1',
89
+ 'email' => 'info@cnpd.lu',
90
+ 'website' => 'http://www.cnpd.lu/',
91
+ ],
92
+ 'MT' => [
93
+ 'phone' => '+356 2328 7100',
94
+ 'email' => 'commissioner.dataprotection@gov.mt',
95
+ 'website' => 'http://www.dataprotection.gov.mt/',
96
+ ],
97
+ 'NL' => [
98
+ 'phone' => '+31 70 888 8500',
99
+ 'email' => 'info@autoriteitpersoonsgegevens.nl',
100
+ 'website' => 'https://autoriteitpersoonsgegevens.nl/nl',
101
+ ],
102
+ 'PL' => [
103
+ 'phone' => '+48 22 53 10 440',
104
+ 'email' => 'kancelaria@giodo.gov.pl',
105
+ 'website' => 'http://www.giodo.gov.pl/',
106
+ ],
107
+ 'PT' => [
108
+ 'phone' => '+351 21 392 84 00',
109
+ 'email' => 'geral@cnpd.pt',
110
+ 'website' => 'http://www.cnpd.pt/',
111
+ ],
112
+ 'RO' => [
113
+ 'phone' => '+40 21 252 5599',
114
+ 'email' => 'anspdcp@dataprotection.ro',
115
+ 'website' => 'http://www.dataprotection.ro/',
116
+ ],
117
+ 'SK' => [
118
+ 'phone' => '+421 2 32 31 32 14',
119
+ 'email' => 'statny.dozor@pdp.gov.sk',
120
+ 'website' => 'http://www.dataprotection.gov.sk/',
121
+ ],
122
+ 'SI' => [
123
+ 'phone' => '+386 1 230 9730',
124
+ 'email' => 'gp.ip@ip-rs.si',
125
+ 'website' => 'https://www.ip-rs.si/',
126
+ ],
127
+ 'ES' => [
128
+ 'phone' => '+34 91399 6200',
129
+ 'email' => 'internacional@agpd.es',
130
+ 'website' => 'https://www.agpd.es/',
131
+ ],
132
+ 'SE' => [
133
+ 'phone' => '+46 8 657 6100',
134
+ 'email' => 'datainspektionen@datainspektionen.se',
135
+ 'website' => 'http://www.datainspektionen.se/',
136
+ ],
137
+ 'UK' => [
138
+ 'phone' => '+44 1625 545 745 ',
139
+ 'email' => 'international.team@ico.org.uk ',
140
+ 'website' => 'https://ico.org.uk',
141
+ ],
142
+
143
+ /* EFTA countries */
144
+
145
+ 'IS' => [
146
+ 'phone' => '+354 510 9600',
147
+ 'email' => 'postur@personuvernd.is',
148
+ 'website' => 'http://personuvernd.is/',
149
+ ],
150
+ 'LI' => [
151
+ 'phone' => '+423 236 6090',
152
+ 'email' => 'info.dss@llv.li',
153
+ 'website' => 'http://www.dss.llv.li/',
154
+ ],
155
+ 'NO' => [
156
+ 'phone' => '+47 22 39 69 00',
157
+ 'email' => 'postkasse@datatilsynet.no',
158
+ 'website' => 'http://www.datatilsynet.no/',
159
+ ],
160
+ 'CH' => [
161
+ 'phone' => '+41 31 322 4395',
162
+ 'email' => 'contact20@edoeb.admin.ch',
163
+ 'website' => 'http://www.edoeb.admin.ch/',
164
+ ],
165
+
166
+ /* Others - default to Ireland */
167
+
168
+ 'UK' => [
169
+ 'phone' => '+353 57 868 4800',
170
+ 'email' => 'info@dataprotection.ie',
171
+ 'website' => 'https://www.dataprotection.ie/',
172
+ ],
173
+ 'US' => [
174
+ 'phone' => '+353 57 868 4800',
175
+ 'email' => 'info@dataprotection.ie',
176
+ 'website' => 'https://www.dataprotection.ie/',
177
+ ],
178
+ 'other' => [
179
+ 'phone' => '+353 57 868 4800',
180
+ 'email' => 'info@dataprotection.ie',
181
+ 'website' => 'https://www.dataprotection.ie/',
182
+ ],
183
+ ];
assets/gdpr-admin.css ADDED
@@ -0,0 +1,362 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /** Admin notice **/
2
+ .notice-gdpr {
3
+ border-left-color: #0095a8;
4
+ overflow: hidden;
5
+ }
6
+
7
+ .notice-gdpr img {
8
+ width: 100px;
9
+ margin: 25px 0;
10
+ float: left;
11
+ }
12
+
13
+ .notice-gdpr .gdpr-content {
14
+ float: left;
15
+ width: calc(100% - 120px);
16
+ padding-left: 20px;
17
+ padding-bottom: 15px;
18
+ }
19
+
20
+ /** Modal window **/
21
+ .gdpr-modal {
22
+ max-width: 1200px;
23
+ }
24
+
25
+ .gdpr-modal li {
26
+ list-style-type: disc;
27
+ margin-left: 20px;
28
+ }
29
+
30
+ .gdpr-modal-footer {
31
+ background: #fcfcfc;
32
+ border-top: 1px solid #dfdfdf;
33
+ padding: 10px 0;
34
+ }
35
+
36
+ /** General **/
37
+ .gdpr-framework-wrap table {
38
+ table-layout: fixed;
39
+ border-spacing: 0;
40
+ }
41
+
42
+ .gdpr-framework-wrap input {
43
+ margin: 0;
44
+ }
45
+
46
+ .gdpr-framework-wrap .gdpr-select {
47
+ width: 300px;
48
+ max-width: 90%;
49
+ height: 32px;
50
+ border-color: #ddd;
51
+ background-color: #fff;
52
+ border: 1px solid #aaa;
53
+ border-radius: 4px;
54
+ box-sizing: border-box;
55
+ cursor: pointer;
56
+ display: block;
57
+ padding: 0 6px;
58
+ color: #666;
59
+ }
60
+
61
+ .gdpr-framework-wrap .select2-container {
62
+ display: block;
63
+ width: 300px;
64
+ max-width: 100%;
65
+
66
+ }
67
+
68
+ .gdpr-framework-wrap .select2-container--default .select2-selection--single .select2-selection__arrow,
69
+ .gdpr-framework-wrap .select2-container--default .select2-selection--single {
70
+ height: 32px !important;
71
+ }
72
+
73
+ .gdpr-framework-wrap .select2-container--default .select2-selection--single .select2-selection__arrow b {
74
+ border-width: 6px 3px 0 3px;
75
+ margin-left: -2px;
76
+ margin-top: -3px;
77
+ border-color: #666 transparent transparent transparent;
78
+ }
79
+
80
+ .gdpr-framework-wrap .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
81
+ border-color: transparent transparent #666 transparent;
82
+ border-width: 0 3px 6px 3px;
83
+ }
84
+
85
+ .gdpr-framework-wrap input {
86
+ border-radius: 4px;
87
+ border: 1px solid #aaa;
88
+ box-shadow: none;
89
+ height: 32px;
90
+ line-height: 28px;
91
+ min-width: 300px;
92
+ max-width: 100%;
93
+ }
94
+
95
+ .gdpr-framework-wrap textarea {
96
+ border-radius: 4px;
97
+ border: 1px solid #aaa;
98
+ box-shadow: none;
99
+ height: 32px;
100
+ line-height: 28px;
101
+ }
102
+
103
+ .gdpr-framework-wrap .select2-container input {
104
+ min-width: 0;
105
+ }
106
+
107
+ .gdpr-framework-wrap input[type=checkbox] {
108
+ line-height: initial;
109
+ height: 16px;
110
+ min-width: 0;
111
+ }
112
+
113
+ .gdpr-framework-wrap input[type=submit] {
114
+ min-width: 0;
115
+ }
116
+
117
+ .gdpr-framework-wrap input[type=button] {
118
+ min-width: 0;
119
+ }
120
+
121
+ .gdpr-framework-wrap .wp-editor-wrap {
122
+ width: 720px;
123
+ max-width: 100%;
124
+ }
125
+
126
+ .gdpr-framework-wrap .nav-tab-highlight {
127
+ background: #ffa200;
128
+ color: #fff;
129
+ font-weight: 700;
130
+ border-bottom: 1px solid #ccc;
131
+ }
132
+
133
+ .gdpr-framework-wrap .nav-tab-highlight:hover {
134
+ background: #ffc35c;
135
+ color: #fff;
136
+ }
137
+
138
+ .gdpr-framework-wrap .nav-tab-highlight:focus {
139
+ box-shadow: 0 0 2px 1px #5b9cd9;
140
+ }
141
+
142
+ /* Consent tables */
143
+ .gdpr-framework-wrap .gdpr-consent,
144
+ .gdpr-framework-wrap .gdpr-consent-admin {
145
+ table-layout: auto;
146
+ }
147
+
148
+ .gdpr-consent-user {
149
+ border-collapse: collapse;
150
+ }
151
+
152
+ .gdpr-consent-user th {
153
+ padding-bottom: 10px;
154
+ }
155
+
156
+ .gdpr-consent-user td,
157
+ .gdpr-consent-user th {
158
+ border: 1px solid #ccc;
159
+ }
160
+
161
+ .gdpr-consent-user tr:first-child th {
162
+ border: 0;
163
+ }
164
+
165
+ .gdpr-consent th {
166
+ text-align: left;
167
+ }
168
+
169
+ .gdpr-consent td {
170
+ padding-right: 16px;
171
+ }
172
+
173
+ .gdpr-consent-admin th {
174
+ text-align: left;
175
+ padding-bottom: 16px;
176
+ }
177
+
178
+ .gdpr-consent-admin + .button {
179
+ margin-top: 16px;
180
+ }
181
+
182
+ .gdpr-consent-admin td {
183
+ padding-right: 16px;
184
+ vertical-align: top;
185
+ padding-bottom: 20px;
186
+ }
187
+
188
+ .gdpr-framework-wrap .gdpr-consent-admin input {
189
+ min-width: 0;
190
+ }
191
+
192
+ .gdpr-consent-table-desc {
193
+ width: 450px;
194
+ }
195
+
196
+ .gdpr-consent-table-input {
197
+ width: 200px;
198
+ }
199
+
200
+ .gdpr-consent-table-input input {
201
+ width: 100%;
202
+ }
203
+
204
+ .gdpr-consent-table-desc textarea {
205
+ width: 100%;
206
+ min-height: 100px;
207
+ }
208
+
209
+ .gdpr-consent-add-button {
210
+ text-align: right;
211
+ width: 1051px;
212
+ }
213
+
214
+ .gdpr-consent-user {
215
+ table-layout: auto;
216
+ }
217
+
218
+ .gdpr-consent-user td {
219
+ padding: 15px;
220
+ }
221
+
222
+ .gdpr-consent-user-title {
223
+ width: 200px;
224
+ }
225
+
226
+ .gdpr-consent-user-desc {
227
+ width: 450px;
228
+ }
229
+
230
+ /* support page */
231
+ .gdpr-framework-wrap .align-center {
232
+ text-align: center;
233
+ }
234
+
235
+ .gdpr-framework-wrap .section {
236
+ margin: 25px 0;
237
+ max-width: 720px;
238
+ }
239
+
240
+ .gdpr-framework-wrap .row {
241
+ box-sizing: border-box;
242
+ display: -webkit-box;
243
+ display: -ms-flexbox;
244
+ display: flex;
245
+ -ms-flex-wrap: wrap;
246
+ flex-wrap: wrap;
247
+ margin-right: -8px;
248
+ margin-left: -8px;
249
+ -webkit-box-pack: center !important;
250
+ -ms-flex-pack: center !important;
251
+ justify-content: center !important;
252
+ }
253
+
254
+ .gdpr-framework-wrap .col {
255
+ box-sizing: border-box;
256
+ position: relative;
257
+ width: 100%;
258
+ min-height: 1px;
259
+ padding-right: 8px;
260
+ padding-left: 8px;
261
+ margin-bottom: 15px;
262
+ }
263
+
264
+ @media (min-width: 576px) {
265
+ .gdpr-framework-wrap .col {
266
+ -webkit-box-flex: 0;
267
+ -ms-flex: 0 0 50%;
268
+ flex: 0 0 50%;
269
+ max-width: 50%;
270
+ }
271
+ }
272
+
273
+ @media (min-width: 768px) {
274
+ .gdpr-framework-wrap .col {
275
+ -webkit-box-flex: 0;
276
+ -ms-flex: 0 0 33.33333%;
277
+ flex: 0 0 33.33333%;
278
+ max-width: 33.33333%;
279
+ }
280
+ }
281
+
282
+ .gdpr-framework-wrap .col .button {
283
+ display: block;
284
+ text-align: center;
285
+ }
286
+
287
+ .gdpr-framework-wrap .col_image {
288
+ background-position: center;
289
+ background-repeat: no-repeat;
290
+ background-size: cover;
291
+ border-radius: 6px;
292
+ background-color: #e5e5e5;
293
+ padding-bottom: 60%;
294
+ margin-bottom: 15px;
295
+ position: relative;
296
+ }
297
+
298
+ .gdpr-framework-wrap .col_image:after {
299
+ content: '';
300
+ position: absolute;
301
+ top: 0;
302
+ left: 0;
303
+ width: 100%;
304
+ height: 100%;
305
+ background-color: #ffa200;
306
+ opacity: .15;
307
+ }
308
+
309
+ .gdpr-framework-wrap .col .button {
310
+ font-size: 14px;
311
+ line-height: 28px;
312
+ padding: 4px 15px;
313
+ height: auto;
314
+ border: 1px solid #ccc;
315
+ box-shadow: 0 1px 0 #ccc;
316
+ -webkit-box-shadow: 0 1px 1px #ccc;
317
+ transition: .1s;
318
+ }
319
+
320
+ .gdpr-framework-wrap .col .button:hover {
321
+ box-shadow: 0 1px 1px #ccc;
322
+ -webkit-box-shadow: 0 1px 1px #ccc;
323
+ border: 1px solid #ccc;
324
+ background: #fff;
325
+ }
326
+
327
+ .gdpr-framework-wrap .col .button:focus {
328
+ box-shadow: 0 0 2px 1px #5b9cd9;
329
+ }
330
+
331
+ .gdpr-framework-wrap .col .button-primary {
332
+ background-color: #ffa200;
333
+ border-color: #ffa200;
334
+ color: #fff;
335
+ text-shadow: none;
336
+ -webkit-box-shadow: 0 1px 1px #ccc;
337
+ box-shadow: 0 1px 1px #ccc;
338
+ }
339
+
340
+ .gdpr-framework-wrap .col .button-primary:hover {
341
+ background: #ffc35c;
342
+ border-color: #ffc35c;
343
+ color: #fff;
344
+ }
345
+
346
+ .gdpr-framework-wrap .col .button-primary:active,
347
+ .gdpr-framework-wrap .col .button-primary:focus {
348
+ background-color: #ffa200;
349
+ border-color: #ccc;
350
+ color: #fff;
351
+ box-shadow: 0 0 2px 1px #5b9cd9;
352
+ }
353
+
354
+ .gdpr-framework-wrap .col p {
355
+ font-size: 14px;
356
+ line-height: 26px;
357
+ color: #555;
358
+ }
359
+
360
+ .gdpr-framework-wrap .col p:last-child {
361
+ margin-bottom: 0;
362
+ }
assets/gdpr-admin.js ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function ($) {
2
+
3
+ // Handler to open the modal dialog
4
+ $(document).on('click', '.gdpr-open-modal', function (e) {
5
+ $($(this).data('gdpr-modal-target')).dialog('open');
6
+ e.preventDefault();
7
+ });
8
+
9
+ // Initialize all modals on page
10
+ $('.gdpr-modal').each(function (i, e) {
11
+ var $base = $(this);
12
+
13
+ $base.dialog({
14
+ title: $base.data('gdpr-title'),
15
+ dialogClass: 'wp-dialog',
16
+ autoOpen: false,
17
+ draggable: false,
18
+ width: 'auto',
19
+ modal: true,
20
+ resizable: false,
21
+ closeOnEscape: true,
22
+ position: {
23
+ my: "center",
24
+ at: "center",
25
+ of: window
26
+ },
27
+ create: function () {
28
+ // style fix for WordPress admin
29
+ $('.ui-dialog-titlebar-close').addClass('ui-button');
30
+ },
31
+ open: function () {
32
+ // Bind a click on the overlay to close the dialog
33
+ $('.ui-widget-overlay').bind('click', function () {
34
+ $base.dialog('close');
35
+ });
36
+
37
+ // Bind a custom close button to close the dialog
38
+ $base.find('.gdpr-close-modal').bind('click', function (e) {
39
+ $base.dialog('close');
40
+ e.preventDefault();
41
+ });
42
+
43
+ // Fix overlay CSS issues in admin
44
+ $('.wp-dialog').css('z-index', 9999);
45
+ $('.ui-widget-overlay').css('z-index', 9998);
46
+ },
47
+ close: function () {
48
+ $('.wp-dialog').css('z-index', 101);
49
+ $('.ui-widget-overlay').css('z-index', 100);
50
+ }
51
+ });
52
+ });
53
+
54
+ /**
55
+ * https://github.com/DubFriend/jquery.repeater
56
+ */
57
+ $('.js-gdpr-repeater').each(function () {
58
+ var $repeater = $(this).repeater({
59
+ isFirstItemUndeletable: true
60
+ });
61
+
62
+ if (typeof window.repeaterData[$(this).data('name')] !== undefined) {
63
+ $repeater.setList(window.repeaterData[$(this).data('name')]);
64
+ }
65
+ });
66
+
67
+ /**
68
+ * Init select2
69
+ */
70
+ $('.js-gdpr-select2').select2({
71
+ width: 'style'
72
+ });
73
+
74
+ /**
75
+ * Auto-fill DPA info
76
+ */
77
+ $('.js-gdpr-country-selector').on('change', function () {
78
+ var dpaData, $website, $email, $phone;
79
+ var countryCode = $(this).val();
80
+
81
+ if (!window.gdprDpaData[countryCode]) {
82
+ return;
83
+ }
84
+
85
+ dpaData = window.gdprDpaData[countryCode];
86
+
87
+ $website = $('#gdpr_dpa_website');
88
+ if ('' === $website.data('set')) {
89
+ $website.val(dpaData['website']);
90
+ }
91
+
92
+ $email = $('#gdpr_dpa_email');
93
+ if ('' === $email.data('set')) {
94
+ $email.val(dpaData['email']);
95
+ }
96
+
97
+ $phone = $('#gdpr_dpa_phone');
98
+ if ('' === $phone.data('set')) {
99
+ $phone.val(dpaData['phone']);
100
+ }
101
+ });
102
+ });
assets/gdpr-badge.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="500" height="249" viewBox="0 0 500 249"><defs><path id="dqmjc" d="M821.36 463.77l19.32 19.37-19.32 17.75h57.2v-37.12z"/><path id="dqmjd" d="M1097.73 463.77l-19.32 19.37 19.32 17.75h-57.2v-37.12z"/><path id="dqmje" d="M1058.35 378.87c-29.34-1.53-76.26-10.4-98.8-33.94-22.54 23.55-69.47 32.41-98.8 33.94-17.53 165.26 93.49 195.12 98.8 196.47 5.3-1.35 116.32-31.2 98.8-196.47"/><path id="dqmjf" d="M865.1 383.35c11.14-.8 29.47-2.93 48.95-8.65 19.54-5.74 34.82-13.51 45.5-23.14 10.66 9.63 25.94 17.4 45.48 23.14 19.48 5.72 37.8 7.85 48.95 8.65 3.36 35.52.84 67.25-7.51 94.36-6.92 22.47-17.85 41.86-32.5 57.66-23 24.82-48.17 33.26-54.43 35.06-6.26-1.8-31.42-10.24-54.44-35.06-14.64-15.8-25.57-35.2-32.49-57.66-8.35-27.1-10.87-58.84-7.51-94.36zm95.63 196.58c3.34-.85 33.41-9.15 60.3-38.15 15.61-16.85 27.25-37.47 34.59-61.3 9.04-29.34 11.55-63.7 7.48-102.1l-.43-4.03-4.08-.22c-31.31-1.62-75.13-11.1-95.58-32.46l-3.47-3.63-3.47 3.63c-20.45 21.36-64.27 30.84-95.58 32.46l-4.08.22-.43 4.02c-4.07 38.41-1.56 72.77 7.48 102.11 7.34 23.83 18.98 44.45 34.6 61.3 26.88 29 56.95 37.3 60.29 38.15l1.2.3z"/><path id="dqmjg" d="M1041.65 392.6c-24.38-1.26-63.37-8.63-82.1-28.2-18.73 19.57-57.73 26.94-82.1 28.2-14.57 137.33 77.69 162.14 82.1 163.26 4.4-1.12 96.66-25.93 82.1-163.26"/><path id="dqmjh" d="M848.07 449.47v39.24a569.76 569.76 0 0 0 222.95 0v-39.24a569.76 569.76 0 0 1-222.95 0z"/><path id="dqmji" d="M956.6 453.98l2.97-2.14 2.96 2.14-1.12-3.47 3-2.14h-3.7l-1.14-3.51-1.14 3.51h-3.71l3.01 2.14z"/><path id="dqmjj" d="M941.25 458.06l2.97-2.14 2.96 2.14-1.12-3.46 3-2.15h-3.7l-1.14-3.51-1.14 3.52h-3.71l3.01 2.14z"/><path id="dqmjk" d="M932.99 460.09l-1.14 3.52h-3.71l3.01 2.14-1.13 3.46 2.97-2.14 2.96 2.14-1.12-3.46 3-2.15h-3.7z"/><path id="dqmjl" d="M928.87 482.25l2.96 2.14-1.12-3.46 3-2.15h-3.7l-1.14-3.51-1.14 3.52h-3.71l3.01 2.14-1.13 3.46z"/><path id="dqmjm" d="M934.12 494.01L933 490.5l-1.14 3.52h-3.71l3.01 2.14-1.13 3.46 2.97-2.14 2.96 2.14-1.12-3.46 3-2.15z"/><path id="dqmjn" d="M945.37 505.16l-1.13-3.51-1.14 3.51h-3.71l3.01 2.14-1.13 3.47 2.97-2.14 2.96 2.14-1.12-3.47 3-2.14z"/><path id="dqmjo" d="M960.7 509.2l-1.13-3.52-1.14 3.52h-3.71l3.01 2.14-1.13 3.46 2.97-2.14 2.96 2.14-1.12-3.46 3-2.15z"/><path id="dqmjp" d="M976.03 505.16l-1.13-3.51-1.14 3.51h-3.71l3 2.14-1.12 3.47 2.97-2.14 2.96 2.14-1.13-3.47 3.02-2.14z"/><path id="dqmjq" d="M987.29 494.01l-1.14-3.51-1.14 3.52h-3.71l3.01 2.14-1.13 3.46 2.97-2.14 2.96 2.14-1.13-3.46L991 494z"/><path id="dqmjr" d="M995.07 478.74h-3.71l-1.14-3.51-1.14 3.52h-3.71l3.01 2.14-1.12 3.46 2.96-2.14 2.96 2.14-1.12-3.46z"/><path id="dqmjs" d="M983.18 469.16l2.97-2.14 2.96 2.14-1.13-3.47 3.02-2.14h-3.71l-1.14-3.51-1.14 3.52h-3.71l3.01 2.13z"/><path id="dqmjt" d="M974.94 448.94l-1.14 3.52-3.71-.01 3 2.15-1.12 3.46 2.97-2.14 2.96 2.14-1.12-3.46 3-2.15h-3.7z"/><path id="dqmju" d="M1153.56 367.84c0 18.13 6.75 32.81 15.09 32.81.67 0 1.33-.08 1.97-.27l5.5-1.27c-.8 0-1.58-.13-2.32-.43-6.56-2.4-11.55-14.8-11.55-29.76 0-4.54.46-8.86 1.27-12.71-3.18.9-7 4.63-8.42 6.32l-1.44 1.75c-.06 1.16-.1 2.36-.1 3.56"/><path id="dqmjv" d="M1162.25 368.92c0 14.96 5 27.36 11.55 29.76.75.3 1.53.43 2.32.43 6.7 0 12.3-10.38 13.58-24.16.17-1.94.28-3.96.28-6.03v-.36c0-.87-.04-1.75-.07-2.6a60.54 60.54 0 0 0-1.23-9.75 41.15 41.15 0 0 0-2.84-8.76c-.77 1-1.57 2.06-2.35 3.17 1.78 4.54 3.03 10.76 3.03 18.3 0 15.92-5.57 25.92-9.86 26.71-.18.03-.37.05-.54.05-4.34 0-10.41-10.18-10.41-26.76 0-4.88.53-9.22 1.38-12.9-.86-.02-1.64-.02-2.25-.02a4.2 4.2 0 0 0-1.3.2h-.01a62.16 62.16 0 0 0-1.28 12.72"/><path id="dqmjw" d="M1085.88 367.84c0 18.13 6.75 32.81 15.07 32.81h67.7c-8.34 0-15.09-14.68-15.09-32.81 0-1.2.04-2.4.1-3.56-.55.63-.89.95-1.46 1.14-.48.16-15.86.16-31.19.12l-5.03-.01c-13.63-.04-25.91-.1-25.91-.1s-2.48-.97-3.86-4.41v.03a66.92 66.92 0 0 0-.33 6.79"/><path id="dqmjx" d="M1165.71 368.91c0 16.58 6.07 26.77 10.4 26.77.18 0 .37-.02.55-.05 4.28-.79 9.86-10.79 9.86-26.72 0-7.53-1.25-13.75-3.03-18.29l-16.4 5.4a58.54 58.54 0 0 0-1.38 12.9"/><path id="dqmjy" d="M1171.07 368.92c0 6.46 2.46 11.7 5.49 11.7 3.02 0 5.46-5.24 5.46-11.7 0-6.47-2.44-11.7-5.46-11.7-3.03 0-5.49 5.23-5.49 11.7"/><path id="dqmjz" d="M1086.21 361.05v-.03c1.38 3.44 3.86 4.4 3.86 4.4s12.28.07 25.91.1l5.03.02c15.33.04 30.7.04 31.2-.12.56-.19.9-.51 1.46-1.14l1.44-1.75c1.42-1.7 5.24-5.42 8.42-6.32h.02a5.32 5.32 0 0 1 1.3-.2h2.25c3.92.04 9.79.13 10.96.13 1.46 0 2.17-.83 3.3-2.3a74.11 74.11 0 0 1 4.48-6.4c4.8-6.16 9.31-9.84 12.77-12 3.43-2.12 5.83-2.76 6.44-2.81h-5.09l-9.46.02c-22.7.06-78.91.2-86.47 0-12.6-.33-18.72 14.23-18.72 23.23 0 2.16.37 3.85.9 5.17"/><path id="dqmja" d="M1185.85 332.63h23.24v23.83h-23.24z"/><path id="dqmjB" d="M1185.85 347.45a41.1 41.1 0 0 1 2.84 8.76s8.37.58 9.53 0c1.16-.59 5.92-7.63 8.65-13 3.32-6.5 2.21-7.96 1.2-9.4-.58-.84-1.34-1.11-3.02-1.18-.6.05-3 .69-6.43 2.81-3.46 2.16-7.98 5.84-12.77 12.01"/><path id="dqmjb" d="M710 580.23h499.09v-247.6H710z"/><path id="dqmjD" d="M1059.26 433.17h9.51v-40.68h-9.51z"/><path id="dqmjE" d="M1068.78 406.04v13.57h35.21a3.44 3.44 0 0 1-.38-1.58v-11.99z"/><path id="dqmjF" d="M1103.61 398.33v19.7a3.45 3.45 0 0 0 3.46 3.43h12.44c1.9 0 3.46-1.53 3.46-3.43v-19.7a3.45 3.45 0 0 0-3.46-3.43h-12.44a3.44 3.44 0 0 0-3.46 3.43"/><path id="dqmjG" d="M1086.23 368.44v-.03c1.38 3.44 3.85 4.4 3.85 4.4s12.28.07 25.92.1l5.03.02c15.32.04 30.7.04 31.19-.12.57-.19.91-.51 1.45-1.13l.02-.02 1.43-1.74c1.27-1.51 4.21-2.74 7.14-4.05.2-2.74.75-7.23 1.27-9.66-.4.12-.81.27-1.21.46a17.3 17.3 0 0 0-2.48 1.5 28.95 28.95 0 0 0-4.72 4.36c-.64.77-1.09 1.36-1.43 1.75l-.02.02c-.14.15-.26.29-.38.4-.12.14-.24.25-.35.32-.22.2-.45.32-.72.42-.49.16-15.87.16-31.19.1h-5.05c-13.62-.03-25.91-.08-25.91-.08s-2.48-.96-3.86-4.4a70.5 70.5 0 0 0-.33 6.39c.1.36.22.68.35 1"/><path id="dqmjH" d="M1179.98 355.4c1.49.02 3.17.12 5.09.33 1.12.12 2.31.29 3.61.48a41.14 41.14 0 0 0-2.84-8.76 86.7 86.7 0 0 0-4.48 6.4c-.51.66-.93 1.2-1.38 1.55"/><path id="dqmjI" d="M1162.44 363.91c1.49-1.11 2.68-2.22 3.82-3.25 2.18-1.97 4.15-3.65 7.44-4.54l-6.6-.07h-2.26c-.2 0-.41 0-.64.03-.2.04-.43.09-.65.16h-.02c-.5 2.4-.88 4.98-1.09 7.67"/><path id="dqmjJ" d="M1103.61 398.33v19.7a3.43 3.43 0 0 0 3.46 3.43h1.07c0-20.32.07-20.82 4.9-20.82h9.93v-2.31a3.47 3.47 0 0 0-3.46-3.43h-12.44a3.45 3.45 0 0 0-3.46 3.43"/><path id="dqmjK" d="M765.53 367.84c0 18.13-6.75 32.81-15.09 32.81-.67 0-1.33-.08-1.97-.27l-5.5-1.27c.8 0 1.57-.13 2.32-.43 6.56-2.4 11.55-14.8 11.55-29.76 0-4.54-.46-8.86-1.28-12.71 3.19.9 7 4.63 8.43 6.32l1.43 1.75c.07 1.16.1 2.36.1 3.56"/><path id="dqmjL" d="M756.84 368.92c0 14.96-5 27.36-11.56 29.76-.74.3-1.52.43-2.31.43-6.7 0-12.3-10.38-13.58-24.16a68.36 68.36 0 0 1-.28-6.03v-.36a63.7 63.7 0 0 1 1.3-12.35c.7-3.3 1.66-6.26 2.84-8.76.77 1 1.57 2.06 2.34 3.17-1.77 4.54-3.02 10.76-3.02 18.3 0 15.92 5.57 25.92 9.86 26.71.17.03.36.05.54.05 4.34 0 10.4-10.18 10.4-26.76 0-4.88-.53-9.22-1.38-12.9.87-.02 1.65-.02 2.25-.02a4.2 4.2 0 0 1 1.3.2h.02a62.16 62.16 0 0 1 1.28 12.72"/><path id="dqmjM" d="M833.21 367.84c0 18.13-6.75 32.81-15.07 32.81h-67.7c8.34 0 15.09-14.68 15.09-32.81 0-1.2-.04-2.4-.1-3.56.55.63.89.95 1.46 1.14.48.16 15.86.16 31.19.12l5.03-.01c13.63-.04 25.91-.1 25.91-.1s2.47-.97 3.86-4.41v.03c.22 2.18.33 4.46.33 6.79"/><path id="dqmjN" d="M753.38 368.91c0 16.58-6.06 26.77-10.4 26.77-.18 0-.37-.02-.54-.05-4.29-.79-9.87-10.79-9.87-26.72 0-7.53 1.25-13.75 3.03-18.29l16.4 5.4a58.54 58.54 0 0 1 1.38 12.9"/><path id="dqmjO" d="M748.02 368.92c0 6.46-2.45 11.7-5.48 11.7-3.03 0-5.47-5.24-5.47-11.7 0-6.47 2.44-11.7 5.47-11.7s5.48 5.23 5.48 11.7"/><path id="dqmjP" d="M832.87 361.05v-.03c-1.38 3.44-3.85 4.4-3.85 4.4s-12.29.07-25.92.1l-5.03.02c-15.32.04-30.7.04-31.19-.12-.57-.19-.91-.51-1.47-1.14l-1.44-1.75c-1.41-1.7-5.23-5.42-8.42-6.32h-.02a5.35 5.35 0 0 0-1.3-.2H752c-3.93.04-9.8.13-10.97.13-1.45 0-2.16-.83-3.3-2.3a74.64 74.64 0 0 0-4.48-6.4c-4.8-6.16-9.3-9.84-12.77-12-3.42-2.12-5.83-2.76-6.43-2.81h5.08l9.47.02c22.69.06 78.9.2 86.47 0 12.6-.33 18.72 14.23 18.72 23.23 0 2.16-.37 3.85-.9 5.17"/><path id="dqmjQ" d="M733.24 347.45a41.1 41.1 0 0 0-2.83 8.76s-8.38.58-9.53 0c-1.16-.59-5.92-7.63-8.65-13-3.32-6.5-2.22-7.96-1.21-9.4.58-.84 1.34-1.11 3.02-1.18.6.05 3.01.69 6.44 2.81 3.46 2.16 7.97 5.84 12.76 12.01"/><path id="dqmjR" d="M850.31 433.17h9.51v-40.68h-9.51z"/><path id="dqmjS" d="M850.31 406.04v13.57H815.1c.24-.46.38-1.01.38-1.58v-11.99z"/><path id="dqmjT" d="M815.48 398.33v19.7a3.45 3.45 0 0 1-3.46 3.43h-12.44a3.44 3.44 0 0 1-3.46-3.43v-19.7a3.45 3.45 0 0 1 3.46-3.43h12.44a3.44 3.44 0 0 1 3.46 3.43"/><path id="dqmjU" d="M832.87 368.44v-.03c-1.39 3.44-3.86 4.4-3.86 4.4s-12.28.07-25.92.1l-5.03.02c-15.32.04-30.7.04-31.18-.12-.58-.19-.92-.51-1.46-1.13l-.02-.02-1.43-1.74c-1.26-1.51-4.21-2.74-7.13-4.05-.21-2.74-.76-7.23-1.28-9.66.4.12.82.27 1.21.46a17.16 17.16 0 0 1 2.48 1.5 28.91 28.91 0 0 1 4.72 4.36c.64.77 1.09 1.36 1.43 1.75l.02.02c.14.15.26.29.38.4.12.14.24.25.35.32.22.2.45.32.73.42.48.16 15.86.16 31.18.1h5.05c13.62-.03 25.91-.08 25.91-.08s2.48-.96 3.86-4.4c.2 2.05.31 4.2.33 6.39-.1.36-.22.68-.34 1"/><path id="dqmjV" d="M739.1 355.4c-1.49.02-3.16.12-5.08.33-1.13.12-2.32.29-3.62.48a41.1 41.1 0 0 1 2.84-8.76 86.7 86.7 0 0 1 4.48 6.4c.52.66.93 1.2 1.38 1.55"/><path id="dqmjW" d="M756.64 363.91c-1.49-1.11-2.68-2.22-3.82-3.25-2.18-1.97-4.15-3.65-7.44-4.54l6.6-.07h2.26a4.13 4.13 0 0 1 1.3.19h.01c.5 2.4.88 4.98 1.1 7.67"/><path id="dqmjX" d="M815.48 398.33v19.7a3.43 3.43 0 0 1-3.46 3.43h-1.07c0-20.32-.07-20.82-4.9-20.82h-9.93v-2.31a3.47 3.47 0 0 1 3.46-3.43h12.44a3.45 3.45 0 0 1 3.46 3.43"/><path id="dqmjY" d="M918.65 422.83v-4.43h11.55v10.48a14.58 14.58 0 0 1-4.88 2.84 17.91 17.91 0 0 1-6.47 1.23c-2.78 0-5.2-.58-7.27-1.73a11.05 11.05 0 0 1-4.65-4.95 15.87 15.87 0 0 1-1.56-7.01c0-2.74.58-5.17 1.74-7.3a11.66 11.66 0 0 1 5.09-4.9c1.7-.87 3.82-1.31 6.36-1.31 3.3 0 5.87.69 7.72 2.06a9.26 9.26 0 0 1 3.58 5.67l-5.33.99a5.57 5.57 0 0 0-2.1-3.06 6.42 6.42 0 0 0-3.87-1.12c-2.35 0-4.2.73-5.6 2.2-1.37 1.48-2.07 3.66-2.07 6.56 0 3.12.7 5.46 2.1 7.02a7.05 7.05 0 0 0 5.51 2.34c1.13 0 2.25-.22 3.38-.65 1.13-.44 2.1-.97 2.9-1.6v-3.33z"/><path id="dqmjZ" d="M944.75 410.65c2.19 0 3.65.08 4.4.25 1 .22 1.83.63 2.48 1.24a6.01 6.01 0 0 1 1.53 2.55c.36 1.08.54 2.65.54 4.68a17 17 0 0 1-.54 4.82 5.56 5.56 0 0 1-1.4 2.54c-.58.51-1.3.87-2.17 1.09-.67.17-1.75.25-3.25.25h-4v-17.42zm-7.77 21.85h10.09c1.98 0 3.56-.18 4.75-.55a9.45 9.45 0 0 0 3.76-2.1 11.57 11.57 0 0 0 2.85-4.65c.54-1.57.81-3.43.81-5.6 0-2.46-.29-4.53-.87-6.21a11.47 11.47 0 0 0-2.53-4.26 8.9 8.9 0 0 0-4-2.43c-1.17-.33-2.85-.5-5.06-.5h-9.8z"/><path id="dqmjaa" d="M972.63 410.65c1.93 0 3.22.06 3.86.18.87.15 1.59.54 2.15 1.17.57.62.86 1.4.86 2.36 0 .78-.2 1.46-.61 2.05-.4.59-.96 1.02-1.68 1.3-.7.27-2.12.4-4.23.4h-2.94v-7.46zm-2.59 21.85v-9.92h3.5c2.42 0 4.28-.12 5.56-.38a7.8 7.8 0 0 0 2.78-1.24 7.24 7.24 0 0 0 2.25-2.6c.6-1.1.9-2.45.9-4.07 0-2.1-.52-3.8-1.55-5.12a6.96 6.96 0 0 0-3.82-2.58c-1-.26-3.12-.39-6.38-.39h-8.6v26.3z"/><path id="dqmjab" d="M995.21 410.65h4.19c2.17 0 3.47.03 3.9.09a3.3 3.3 0 0 1 2.03 1.03c.47.55.71 1.26.71 2.15a3.4 3.4 0 0 1-.54 1.97c-.36.53-.86.9-1.5 1.11-.64.22-2.25.32-4.82.32h-3.97zm0 21.85v-10.98h1.09c1.23 0 2.13.1 2.7.3.56.2 1.1.56 1.61 1.1.5.53 1.45 1.81 2.83 3.86l3.87 5.72h6.41l-3.24-5.13a29.93 29.93 0 0 0-3.05-4.27c-.76-.8-1.71-1.53-2.87-2.2 2.33-.34 4.08-1.15 5.25-2.44a6.98 6.98 0 0 0 1.76-4.89c0-1.55-.39-2.93-1.16-4.14a6.1 6.1 0 0 0-3.09-2.52c-1.29-.47-3.35-.71-6.19-.71h-11.28v26.3z"/><clipPath id="dqmjA"><use xlink:href="#dqmja"/></clipPath><clipPath id="dqmjC"><use xlink:href="#dqmjb"/></clipPath></defs><g><g transform="translate(-710 -332)"><use fill="#1a3e51" xlink:href="#dqmjc"/></g><g transform="translate(-710 -332)"><use fill="#1a3e51" xlink:href="#dqmjd"/></g><g transform="translate(-710 -332)"><use fill="#1a3e51" xlink:href="#dqmje"/></g><g transform="translate(-710 -332)"><use fill="#559e8b" xlink:href="#dqmjf"/></g><g transform="translate(-710 -332)"><use fill="#559e8b" xlink:href="#dqmjg"/></g><g transform="translate(-710 -332)"><use fill="#164193" xlink:href="#dqmjh"/></g><g transform="translate(-710 -332)"><use fill="#fe0" xlink:href="#dqmji"/></g><g transform="translate(-710 -332)"><use fill="#fe0" xlink:href="#dqmjj"/></g><g transform="translate(-710 -332)"><use fill="#fe0" xlink:href="#dqmjk"/></g><g transform="translate(-710 -332)"><use fill="#fe0" xlink:href="#dqmjl"/></g><g transform="translate(-710 -332)"><use fill="#fe0" xlink:href="#dqmjm"/></g><g transform="translate(-710 -332)"><use fill="#fe0" xlink:href="#dqmjn"/></g><g transform="translate(-710 -332)"><use fill="#fe0" xlink:href="#dqmjo"/></g><g transform="translate(-710 -332)"><use fill="#fe0" xlink:href="#dqmjp"/></g><g transform="translate(-710 -332)"><use fill="#fe0" xlink:href="#dqmjq"/></g><g transform="translate(-710 -332)"><use fill="#fe0" xlink:href="#dqmjr"/></g><g transform="translate(-710 -332)"><use fill="#fe0" xlink:href="#dqmjs"/></g><g transform="translate(-710 -332)"><use fill="#fe0" xlink:href="#dqmjt"/></g><g transform="translate(-710 -332)"><use fill="#88868b" xlink:href="#dqmju"/></g><g transform="translate(-710 -332)"><use fill="#afaeb4" xlink:href="#dqmjv"/></g><g transform="translate(-710 -332)"><use fill="#afaeb4" xlink:href="#dqmjw"/></g><g transform="translate(-710 -332)"><use fill="#13869a" xlink:href="#dqmjx"/></g><g transform="translate(-710 -332)"><use fill="#32a4be" xlink:href="#dqmjy"/></g><g transform="translate(-710 -332)"><use fill="#afaeb4" xlink:href="#dqmjz"/></g><g transform="translate(-710 -332)"/><g clip-path="url(#dqmjA)" transform="translate(-710 -332)"><use fill="#88868b" xlink:href="#dqmjB"/></g><g transform="translate(-710 -332)"/><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#9b9b9e" xlink:href="#dqmjD"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#afaeb4" xlink:href="#dqmjE"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#afaeb4" xlink:href="#dqmjF"/></g><g clip-path="url(#dqmjC)" opacity=".4" transform="translate(-710 -332)"><use fill="#5c5b5f" xlink:href="#dqmjG"/></g><g clip-path="url(#dqmjC)" opacity=".4" transform="translate(-710 -332)"><use fill="#5c5b5f" xlink:href="#dqmjH"/></g><g clip-path="url(#dqmjC)" opacity=".4" transform="translate(-710 -332)"><use fill="#5c5b5f" xlink:href="#dqmjI"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#878787" xlink:href="#dqmjJ"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#88868b" xlink:href="#dqmjK"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#afaeb4" xlink:href="#dqmjL"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#afaeb4" xlink:href="#dqmjM"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#13869a" xlink:href="#dqmjN"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#32a4be" xlink:href="#dqmjO"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#afaeb4" xlink:href="#dqmjP"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#88868b" xlink:href="#dqmjQ"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#9b9b9e" xlink:href="#dqmjR"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#afaeb4" xlink:href="#dqmjS"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#afaeb4" xlink:href="#dqmjT"/></g><g clip-path="url(#dqmjC)" opacity=".4" transform="translate(-710 -332)"><use fill="#5c5b5f" xlink:href="#dqmjU"/></g><g clip-path="url(#dqmjC)" opacity=".4" transform="translate(-710 -332)"><use fill="#5c5b5f" xlink:href="#dqmjV"/></g><g clip-path="url(#dqmjC)" opacity=".4" transform="translate(-710 -332)"><use fill="#5c5b5f" xlink:href="#dqmjW"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#878787" xlink:href="#dqmjX"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#fff" xlink:href="#dqmjY"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#fff" xlink:href="#dqmjZ"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#fff" xlink:href="#dqmjaa"/></g><g clip-path="url(#dqmjC)" transform="translate(-710 -332)"><use fill="#fff" xlink:href="#dqmjab"/></g></g></svg>
assets/gdpr-installer.css ADDED
@@ -0,0 +1,550 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .gdpr-installer-container {
2
+ max-width: 720px;
3
+ margin: 30px auto;
4
+ font-size: 14px;
5
+ }
6
+
7
+ .gdpr-step-button-prev {
8
+ display: block;
9
+ float: left;
10
+ }
11
+
12
+ .gdpr-step-button-next {
13
+ display: block;
14
+ float: right;
15
+ }
16
+
17
+ .gdpr-header {
18
+ margin: 0 auto 30px;
19
+ overflow: hidden;
20
+ padding: 0 16px;
21
+ text-align: center;
22
+ }
23
+
24
+ .gdpr-header_left {
25
+ float: left;
26
+ width: 100%;
27
+ }
28
+
29
+ .gdpr-header_right {
30
+ float: left;
31
+ width: 100%;
32
+ }
33
+
34
+ @media (min-width: 576px) {
35
+ .gdpr-header {
36
+ text-align: left;
37
+ }
38
+
39
+ .gdpr-header_left {
40
+ width: 175px;
41
+ padding-right: 15px;
42
+ padding-top: 25px;
43
+ }
44
+
45
+ .gdpr-header_right {
46
+ width: calc(100% - 235px);
47
+ }
48
+ }
49
+
50
+ @media (min-width: 768px) {
51
+ .gdpr-header {
52
+ padding: 0;
53
+ }
54
+ }
55
+
56
+ .gdpr-logo {
57
+ width: 100px;
58
+ }
59
+
60
+ .gdpr-header h1 {
61
+ font-size: 1.8em;
62
+ line-height: normal;
63
+ margin-top: 15px;
64
+ margin-bottom: 20px;
65
+ font-weight: 700;
66
+ }
67
+
68
+ .gdpr-header h2 {
69
+ float: left;
70
+ }
71
+
72
+ .gdpr-installer .gdpr-content {
73
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, .13);
74
+ box-shadow: 0 1px 3px rgba(0, 0, 0, .13);
75
+ padding: 16px;
76
+ margin: 0 0 20px;
77
+ background: #fff;
78
+ overflow: hidden;
79
+ zoom: 1;
80
+ }
81
+
82
+ .gdpr-installer .spacer {
83
+ margin-top: 2em;
84
+ }
85
+
86
+ .gdpr-installer .gdpr-content p {
87
+ font-size: 14px;
88
+ line-height: 26px;
89
+ color: #555;
90
+ }
91
+
92
+ .gdpr-installer .gdpr-content p:last-child {
93
+ margin-bottom: 0;
94
+ }
95
+
96
+ .gdpr-content a {
97
+ color: #0095a7;
98
+ font-weight: 700;
99
+ text-decoration: none;
100
+ box-shadow: none;
101
+ }
102
+
103
+ .gdpr-content a:hover,
104
+ .gdpr-content a:focus {
105
+ outline: none;
106
+ text-decoration: none;
107
+ color: #000;
108
+ }
109
+
110
+ .gdpr-installer .gdpr-content li {
111
+ margin-left: 20px;
112
+ list-style-type: disc;
113
+ }
114
+
115
+ .gdpr-installer .gdpr-content select {
116
+ height: 32px;
117
+ border-color: #ddd;
118
+ background-color: #fff;
119
+ border: 1px solid #aaa;
120
+ border-radius: 4px;
121
+ box-sizing: border-box;
122
+ cursor: pointer;
123
+ display: block;
124
+ padding: 0 6px;
125
+ color: #666;
126
+ }
127
+
128
+ .select2-container .select2-selection--single {
129
+ height: 32px !important;
130
+ }
131
+
132
+ .gdpr-installer .gdpr-content h1 {
133
+ font-weight: 700;
134
+ line-height: normal;
135
+ }
136
+
137
+ .gdpr-installer .gdpr-content h2 {
138
+ font-size: 22px;
139
+ line-height: 32px;
140
+ font-weight: 700;
141
+ }
142
+
143
+ .gdpr-installer .gdpr-content h3 {
144
+ font-size: 18px;
145
+ line-height: 22px;
146
+ font-weight: 700;
147
+ }
148
+
149
+ .wp-core-ui .button {
150
+ font-size: 14px;
151
+ line-height: 28px;
152
+ padding: 4px 15px;
153
+ height: auto;
154
+ border: 1px solid #ccc;
155
+ box-shadow: 0 1px 0 #ccc;
156
+ -webkit-box-shadow: 0 1px 1px #ccc;
157
+ transition: .1s;
158
+ }
159
+
160
+ .wp-core-ui .button:hover {
161
+ box-shadow: 0 1px 1px #ccc;
162
+ -webkit-box-shadow: 0 1px 1px #ccc;
163
+ border: 1px solid #ccc;
164
+ background: #fff;
165
+ }
166
+
167
+ .wp-core-ui .button:focus {
168
+ box-shadow: 0 0 2px 1px #5b9cd9;
169
+ }
170
+
171
+ .wp-core-ui .button-gdpr {
172
+ background: #0095a8;
173
+ border-color: #0095a8;
174
+ -webkit-box-shadow: 0 1px 1px #ccc;
175
+ box-shadow: 0 1px 1px #ccc;
176
+ color: #fff;
177
+ text-decoration: none;
178
+ transition: .1s;
179
+ }
180
+
181
+ .wp-core-ui .button-gdpr:hover {
182
+ background: #79c2cb;
183
+ border-color: #79c2cb;
184
+ color: #fff;
185
+ }
186
+
187
+ .wp-core-ui .button-gdpr:active,
188
+ .wp-core-ui .button-gdpr:focus {
189
+ background: #0095a8;
190
+ border-color: #ccc;
191
+ color: #fff;
192
+ box-shadow: 0 0 2px 1px #5b9cd9;
193
+ }
194
+
195
+ .wp-core-ui .button-primary {
196
+ background-color: #ffa200;
197
+ border-color: #ffa200;
198
+ color: #fff;
199
+ text-shadow: none;
200
+ -webkit-box-shadow: 0 1px 1px #ccc;
201
+ box-shadow: 0 1px 1px #ccc;
202
+ }
203
+
204
+ .wp-core-ui .button-primary:hover {
205
+ background: #ffc35c;
206
+ border-color: #ffc35c;
207
+ color: #fff;
208
+ }
209
+
210
+ .wp-core-ui .button-primary:active,
211
+ .wp-core-ui .button-primary:focus {
212
+ background-color: #ffa200;
213
+ border-color: #ccc;
214
+ color: #fff;
215
+ box-shadow: 0 0 2px 1px #5b9cd9;
216
+ }
217
+
218
+ .wp-core-ui.gdpr-installer .button-right {
219
+ float: right;
220
+ }
221
+
222
+ .wp-core-ui .button.button-gdpr-large {
223
+ font-size: 14px;
224
+ padding: 4px 15px;
225
+ height: auto;
226
+ }
227
+
228
+ .gdpr-footer-links {
229
+ text-align: center;
230
+ }
231
+
232
+ .wp-core-ui .button-center {
233
+ display: block;
234
+ margin: 25px auto 10px;
235
+ }
236
+
237
+ .gdpr-installer label {
238
+ color: #666;
239
+ font-size: 14px;
240
+ font-weight: 500;
241
+ margin-bottom: .5em;
242
+ margin-top: 1em;
243
+ display: block;
244
+ cursor: pointer;
245
+ }
246
+
247
+ .gdpr-installer input[type=text],
248
+ .gdpr-installer input[type=email] {
249
+ border: 1px solid #aaa;
250
+ border-color: #ddd;
251
+ border-radius: 4px;
252
+ height: 30px;
253
+ width: calc(100% - 8px - 24px - 2px);
254
+ padding-left: 8px;
255
+ padding-right: 24px;
256
+ color: #444;
257
+ background-color: #fff;
258
+ display: inline-block;
259
+ }
260
+
261
+ .gdpr-installer input + em {
262
+ display: block;
263
+ margin-top: 5px;
264
+ }
265
+
266
+ .gdpr-installer .select2 + em {
267
+ display: block;
268
+ margin-top: 5px;
269
+ }
270
+
271
+ .gdpr-installer textarea {
272
+ width: calc(100% - 8px - 24px - 2px);
273
+ height: 80px;
274
+ }
275
+
276
+ .gdpr-installer .select2-container {
277
+ display: block;
278
+ width: 300px;
279
+ max-width: 100%;
280
+ }
281
+
282
+ .gdpr-select {
283
+ width: 300px;
284
+ max-width: 100%;
285
+ }
286
+
287
+ .hidden {
288
+ display: none;
289
+ }
290
+
291
+ .gdpr-installer fieldset {
292
+ margin: -20px 0 0 0;
293
+ }
294
+
295
+ .wp-core-ui .button-side {
296
+ min-width: 220px;
297
+ text-align: center;
298
+ margin-right: 15px;
299
+ }
300
+
301
+ .align-center {
302
+ text-align: center;
303
+ }
304
+
305
+ .section {
306
+ margin: 25px 0;
307
+ }
308
+
309
+ .row {
310
+ box-sizing: border-box;
311
+ display: -webkit-box;
312
+ display: -ms-flexbox;
313
+ display: flex;
314
+ -ms-flex-wrap: wrap;
315
+ flex-wrap: wrap;
316
+ margin-right: -8px;
317
+ margin-left: -8px;
318
+ -webkit-box-pack: center !important;
319
+ -ms-flex-pack: center !important;
320
+ justify-content: center !important;
321
+ }
322
+
323
+ .col {
324
+ box-sizing: border-box;
325
+ position: relative;
326
+ width: 100%;
327
+ min-height: 1px;
328
+ padding-right: 8px;
329
+ padding-left: 8px;
330
+ margin-bottom: 15px;
331
+ }
332
+
333
+ @media (min-width: 576px) {
334
+ .col {
335
+ -webkit-box-flex: 0;
336
+ -ms-flex: 0 0 50%;
337
+ flex: 0 0 50%;
338
+ max-width: 50%;
339
+ }
340
+ }
341
+
342
+ @media (min-width: 768px) {
343
+ .col {
344
+ -webkit-box-flex: 0;
345
+ -ms-flex: 0 0 33.33333%;
346
+ flex: 0 0 33.33333%;
347
+ max-width: 33.33333%;
348
+ }
349
+ }
350
+
351
+ .wp-core-ui .col .button {
352
+ display: block;
353
+ text-align: center;
354
+ }
355
+
356
+ .col_image {
357
+ background-position: center;
358
+ background-repeat: no-repeat;
359
+ background-size: cover;
360
+ border-radius: 6px;
361
+ background-color: #e5e5e5;
362
+ padding-bottom: 60%;
363
+ margin-bottom: 15px;
364
+ position: relative;
365
+ }
366
+
367
+ .col_image:after {
368
+ content: '';
369
+ position: absolute;
370
+ top: 0;
371
+ left: 0;
372
+ width: 100%;
373
+ height: 100%;
374
+ background-color: #ffa200;
375
+ opacity: .15;
376
+ }
377
+
378
+ /* jQuery UI hacks */
379
+ .ui-tabs {
380
+ position: relative;
381
+ padding: .2em;
382
+ }
383
+
384
+ .ui-tabs-nav {
385
+ margin: 0;
386
+ padding: .2em .2em 0;
387
+ }
388
+
389
+ .ui-helper-clearfix {
390
+ min-height: 0;
391
+ }
392
+
393
+ .ui-helper-reset {
394
+ margin: 0;
395
+ padding: 0;
396
+ border: 0;
397
+ outline: 0;
398
+ line-height: 1.3;
399
+ text-decoration: none;
400
+ font-size: 100%;
401
+ list-style: none;
402
+ }
403
+
404
+ .ui-helper-clearfix:before, .ui-helper-clearfix:after {
405
+ content: "";
406
+ display: table;
407
+ border-collapse: collapse;
408
+ }
409
+
410
+ .ui-helper-clearfix:after {
411
+ clear: both;
412
+ }
413
+
414
+ .ui-tabs .ui-tabs-nav li {
415
+ list-style: none;
416
+ float: left;
417
+ position: relative;
418
+ top: 0;
419
+ margin: 1px .2em 0 0;
420
+ border-bottom-width: 0;
421
+ padding: 0;
422
+ white-space: nowrap;
423
+ background: #eee;
424
+ }
425
+
426
+ .ui-tabs .ui-tabs-nav .ui-tabs-anchor {
427
+ float: left;
428
+ padding: .5em 1em;
429
+ text-decoration: none;
430
+ }
431
+
432
+ .ui-tabs .ui-tabs-nav li.ui-tabs-active {
433
+ background: #fff;
434
+ }
435
+
436
+ .handle {
437
+ cursor: move;
438
+ }
439
+
440
+ .gdpr-table {
441
+ width: 100%;
442
+ }
443
+
444
+ .gdpr-footer-links {
445
+ padding-bottom: 50px;
446
+ }
447
+
448
+ .slidePadding {
449
+ display: block;
450
+ padding-top: 1px;
451
+ padding-bottom: 1px;
452
+ }
453
+
454
+ .gdpr-installer p.error {
455
+ font-size: 16px;
456
+ font-weight: 700;
457
+ color: #e34639;
458
+ }
459
+
460
+ /* gdpr-breadcrumbs */
461
+ .gdpr-breadcrumbs {
462
+ margin-bottom: 22px;
463
+ }
464
+
465
+ .gdpr-breadcrumbs:after {
466
+ display: block;
467
+ clear: both;
468
+ content: '';
469
+ }
470
+
471
+ .gdpr-breadcrumbs_unit {
472
+ box-sizing: border-box;
473
+ float: left;
474
+ width: 50%;
475
+ font-size: 14px;
476
+ line-height: 26px;
477
+ padding-right: 15px;
478
+ }
479
+
480
+ .gdpr-breadcrumbs_unit:nth-child(3),
481
+ .gdpr-breadcrumbs_unit:nth-child(4) {
482
+ margin-top: 10px;
483
+ }
484
+
485
+ .gdpr-breadcrumbs_item {
486
+ background-color: #dddddd;
487
+ padding: 2px 0;
488
+ text-align: center;
489
+ position: relative;
490
+ color: #555555;
491
+ padding-left: 8px;
492
+ }
493
+
494
+ .gdpr-breadcrumbs_item:after {
495
+ position: absolute;
496
+ right: -15px;
497
+ top: 0;
498
+ content: '';
499
+ width: 0;
500
+ height: 0;
501
+ border-style: solid;
502
+ border-width: 15px 0 15px 15px;
503
+ border-color: transparent transparent transparent #dddddd;
504
+ }
505
+
506
+ .gdpr-breadcrumbs_item:before {
507
+ position: absolute;
508
+ left: 0;
509
+ top: 0;
510
+ content: '';
511
+ width: 0;
512
+ height: 0;
513
+ border-style: solid;
514
+ border-width: 15px 0 15px 15px;
515
+ border-color: transparent transparent transparent #f1f1f1;
516
+ }
517
+
518
+ .gdpr-breadcrumbs_unit.active .gdpr-breadcrumbs_item {
519
+ background-color: #0095a8;
520
+ color: #fff;
521
+ font-weight: 700;
522
+ }
523
+
524
+ .gdpr-breadcrumbs_unit.active .gdpr-breadcrumbs_item:after {
525
+ border-color: transparent transparent transparent #0095a8;
526
+ }
527
+
528
+ .gdpr-breadcrumbs_unit:first-child .gdpr-breadcrumbs_item:before,
529
+ .gdpr-breadcrumbs_unit:last-child .gdpr-breadcrumbs_item:after {
530
+ display: none;
531
+ }
532
+
533
+ .gdpr-breadcrumbs_unit:last-child {
534
+ padding: 0;
535
+ }
536
+
537
+ .gdpr-breadcrumbs_unit:last-child .gdpr-breadcrumbs_item {
538
+ padding-left: 0;
539
+ }
540
+
541
+ @media (min-width: 576px) {
542
+ .gdpr-breadcrumbs_unit {
543
+ width: 25%;
544
+ }
545
+
546
+ .gdpr-breadcrumbs_unit:nth-child(3),
547
+ .gdpr-breadcrumbs_unit:nth-child(4) {
548
+ margin-top: 0;
549
+ }
550
+ }
assets/gdpr-installer.js ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function ($) {
2
+
3
+ /**
4
+ * Init select2
5
+ */
6
+ $('.js-gdpr-select2').select2({
7
+ width: 'style'
8
+ });
9
+
10
+ $('#tabs').tabs();
11
+
12
+ $(".sortable").sortable();
13
+
14
+ /**
15
+ * https://github.com/DubFriend/jquery.repeater
16
+ */
17
+ $repeater = $('.js-gdpr-repeater');
18
+ if ($repeater.length) {
19
+ $repeater.repeater({
20
+ ready: function (setIndexes) {
21
+ $(".sortable").on('sortupdate', setIndexes);
22
+ }
23
+ });
24
+
25
+ if (typeof window.gdprConsentTypes !== undefined) {
26
+ $repeater.setList(window.gdprConsentTypes);
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Auto-fill DPA info
32
+ */
33
+ $('.js-gdpr-country-selector').on('change', function () {
34
+ var dpaData, $website, $email, $phone;
35
+ var countryCode = $(this).val();
36
+
37
+ if (!window.gdprDpaData[countryCode]) {
38
+ return;
39
+ }
40
+
41
+ dpaData = window.gdprDpaData[countryCode];
42
+
43
+ $website = $('#gdpr_dpa_website');
44
+ if ('' === $website.data('set')) {
45
+ $website.val(dpaData['website']);
46
+ }
47
+
48
+ $email = $('#gdpr_dpa_email');
49
+ if ('' === $email.data('set')) {
50
+ $email.val(dpaData['email']);
51
+ }
52
+
53
+ $phone = $('#gdpr_dpa_phone');
54
+ if ('' === $phone.data('set')) {
55
+ $phone.val(dpaData['phone']);
56
+ }
57
+ });
58
+ });
assets/gdpr-rhino.svg ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ viewBox="0 0 200 184.16" enable-background="new 0 0 200 184.16" xml:space="preserve">
5
+ <path fill="#0095A8" d="M68.231,30.327l0.963,0.557l0.48-1.003c2.165-4.529,7.729-7.769,16.538-9.631
6
+ c7.685-1.624,14.921-1.569,15.224-1.572l2.189,0.018l-0.753,2.055c-0.159,0.435-3.964,10.733-9.364,18.982l-0.795,1.215l1.41,0.346
7
+ c0.189,0.047,10.648,2.551,24.498,2.551c11.416,0,25.137-1.703,37.298-7.886l0.839-0.427l-0.351-0.874
8
+ c-2.805-6.968-9.737-13.46-20.045-18.775h-0.001c-14.728-7.591-34.4-11.771-55.393-11.771c-14.106,0-27.88,1.921-39.835,5.555
9
+ l-1.864,0.567l1.519,1.219C49.318,18.297,59.321,25.176,68.231,30.327z"/>
10
+ <path fill="#0095A8" d="M43.688,144.994l-0.763,0.659l0.643,0.775c10.468,12.634,22.864,23.576,36.845,32.524l0.554,0.354
11
+ l0.554-0.355c16.89-10.809,31.363-24.421,43.019-40.458c1.847-2.542,3.687-5.243,5.467-8.029l0.694-1.086l-1.213-0.436
12
+ C88.19,114.118,58.004,132.637,43.688,144.994z"/>
13
+ <path fill="#FFA200" d="M188.447,29.518l-1.785-4.136l-0.187,4.501c-0.127,3.055-0.367,6.079-0.714,8.99
14
+ c-1.792,15.057-6.219,26.337-13.159,33.528c-6.032,6.251-13.877,9.226-23.271,8.822c-0.952-0.064-9.353-0.788-11.247-6.271
15
+ c-1.457-4.214,1.406-9.483,8.753-16.11c9.307-8.394,15.282-16.207,18.266-23.883l1.416-3.641l-3.025,2.472
16
+ c-6.804,5.562-18.86,12.356-37.652,13.132c-6.599,0.268-23.328,0.448-36.131-3.678l-2.078-0.67l1.326-1.734
17
+ c4.231-5.538,7.811-13.361,9.568-17.531l0.643-1.526l-1.653,0.1c-5.844,0.354-23.189,1.406-25.986,13.83l-2.015-1.114
18
+ c-15.573-8.61-28.39-18.81-33.177-22.796l-0.457-0.381l-0.558,0.207c-3.508,1.298-6.787,2.73-9.748,4.256
19
+ c-13.84,7.135-21.463,16.3-21.463,25.808c0,0.228,0.113,23.103,9.867,51.417c5.736,16.65,13.638,31.952,23.486,45.481
20
+ c0.835,1.147,1.745,2.354,2.863,3.798l0.663,0.856l0.821-0.706c17.757-15.277,55.164-36.702,106.178-8.553l0.483,0.266l0.489-0.255
21
+ c5.617-2.93,34.134-19.045,43.894-48.605C198.522,68.234,197.038,49.436,188.447,29.518z"/>
22
+ <circle fill="#1F1F1F" cx="113.832" cy="74.693" r="5.619"/>
23
+ <path fill="#1F1F1F" d="M185.298,21.712c-1.757,8.569-3.513,36.397-15.654,47.833c-5.179,5.366-11.962,7.914-20.098,7.571
24
+ c-2.04-0.14-6.484-0.559-7.574-3.508c-0.384-1.11-0.409-4.47,7.621-11.712c0,0,22.418-17.262,17.92-35.129
25
+ c-2.391,2.179-4.928,4.074-7.563,5.728c-3.331-7.659-10.772-14.631-21.704-20.267C122.947,4.343,102.606,0,80.968,0
26
+ C59.33,0,38.99,4.343,23.691,12.229C8.413,20.105,0,30.568,0,41.692c0,0.235,0.115,23.8,10.092,52.757
27
+ c5.868,17.037,13.96,32.703,24.048,46.562c12.275,16.862,27.571,31.093,45.463,42.295l1.365,0.854l1.365-0.854
28
+ c17.929-11.228,33.25-25.491,45.536-42.396c2.123-2.92,4.186-5.982,6.158-9.109c4.267,1.772,8.645,3.896,13.135,6.43l1.151,0.65
29
+ l1.198-0.557c1.456-0.678,35.789-16.997,47.239-51.598C203.77,65.507,199.18,34.458,185.298,21.712z M135.89,16.798
30
+ c10.121,5.219,16.848,11.504,19.563,18.245c-26.884,13.67-61.084,5.254-61.084,5.254c5.462-8.345,9.289-18.701,9.468-19.191
31
+ l1.255-3.425l-3.648-0.03c-0.313-0.003-7.702-0.043-15.445,1.594c-9.158,1.936-14.94,5.356-17.253,10.194
32
+ c-9.919-5.735-19.809-12.765-27.313-18.785c11.811-3.591,25.364-5.511,39.535-5.511C101.802,5.141,121.306,9.282,135.89,16.798z
33
+ M123.709,137.888c-11.584,15.938-25.959,29.456-42.741,40.197c-13.937-8.919-26.209-19.762-36.608-32.313
34
+ c14.376-12.409,44.108-30.461,84.781-15.862C127.393,132.646,125.574,135.323,123.709,137.888z M191.881,85.069
35
+ c-9.592,29.05-37.389,44.883-43.393,48.015c-51.928-28.655-90.002-6.247-107.345,8.674c-0.964-1.244-1.917-2.498-2.845-3.774
36
+ c-9.788-13.445-17.643-28.657-23.345-45.211C5.278,64.692,5.141,41.918,5.141,41.692c0-9.103,7.424-17.944,20.905-24.894
37
+ c3.009-1.55,6.238-2.949,9.634-4.206c8.144,6.78,20.398,15.752,33.337,22.906l3.278,1.813c1.478-11.925,16.557-13.873,25.283-14.402
38
+ c-1.852,4.394-5.332,11.933-9.437,17.306l-2.209,2.891l3.463,1.117c12.994,4.186,29.847,3.997,36.488,3.727
39
+ c19.412-0.801,31.493-7.83,38.261-13.363c-2.078,5.345-6.463,13.09-17.996,23.492c-7.687,6.934-10.642,12.563-9.036,17.21
40
+ c2.111,6.105,11.069,6.888,12.15,6.961c9.72,0.41,17.836-2.664,24.08-9.134c7.099-7.356,11.62-18.836,13.44-34.12
41
+ c0.389-3.264,0.607-6.338,0.72-9.069C195.997,49.619,197.475,68.127,191.881,85.069z"/>
42
+ </svg>
assets/icon-sortable.png ADDED
Binary file
assets/jquery.repeater.min.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ // jquery.repeater version 1.2.1
2
+ // https://github.com/DubFriend/jquery.repeater
3
+ // (MIT) 09-10-2016
4
+ // Brian Detering <BDeterin@gmail.com> (http://www.briandetering.net/)
5
+ !function(a){"use strict";var b=function(a){return a},c=function(b){return a.isArray(b)},d=function(a){return!c(a)&&a instanceof Object},e=function(b,c){return a.inArray(c,b)},f=function(a,b){return e(a,b)!==-1},g=function(a,b){for(var c in a)a.hasOwnProperty(c)&&b(a[c],c,a)},h=function(a){return a[a.length-1]},i=function(a){return Array.prototype.slice.call(a)},j=function(){var a={};return g(i(arguments),function(b){g(b,function(b,c){a[c]=b})}),a},k=function(a,b){var c=[];return g(a,function(a,d,e){c.push(b(a,d,e))}),c},l=function(a,b,c){var d={};return g(a,function(a,e,f){e=c?c(e,a):e,d[e]=b(a,e,f)}),d},m=function(a,b,d){return c(a)?k(a,b):l(a,b,d)},n=function(a,b){return m(a,function(a){return a[b]})},o=function(a,b){var d;return c(a)?(d=[],g(a,function(a,c,e){b(a,c,e)&&d.push(a)})):(d={},g(a,function(a,c,e){b(a,c,e)&&(d[c]=a)})),d},p=function(a,b,c){return m(a,function(a,d){return a[b].apply(a,c||[])})},q=function(a){a=a||{};var b={};return a.publish=function(a,c){g(b[a],function(a){a(c)})},a.subscribe=function(a,c){b[a]=b[a]||[],b[a].push(c)},a.unsubscribe=function(a){g(b,function(b){var c=e(b,a);c!==-1&&b.splice(c,1)})},a};!function(a){var b=function(a,b){var c=q(),d=a.$;return c.getType=function(){throw'implement me (return type. "text", "radio", etc.)'},c.$=function(a){return a?d.find(a):d},c.disable=function(){c.$().prop("disabled",!0),c.publish("isEnabled",!1)},c.enable=function(){c.$().prop("disabled",!1),c.publish("isEnabled",!0)},b.equalTo=function(a,b){return a===b},b.publishChange=function(){var a;return function(d,e){var f=c.get();b.equalTo(f,a)||c.publish("change",{e:d,domElement:e}),a=f}}(),c},i=function(a,c){var d=b(a,c);return d.get=function(){return d.$().val()},d.set=function(a){d.$().val(a)},d.clear=function(){d.set("")},c.buildSetter=function(a){return function(b){a.call(d,b)}},d},j=function(a,b){a=c(a)?a:[a],b=c(b)?b:[b];var d=!0;return a.length!==b.length?d=!1:g(a,function(a){f(b,a)||(d=!1)}),d},k=function(a){var b={},c=i(a,b);return c.getType=function(){return"button"},c.$().on("change",function(a){b.publishChange(a,this)}),c},l=function(b){var d={},e=i(b,d);return e.getType=function(){return"checkbox"},e.get=function(){var b=[];return e.$().filter(":checked").each(function(){b.push(a(this).val())}),b},e.set=function(b){b=c(b)?b:[b],e.$().each(function(){a(this).prop("checked",!1)}),g(b,function(a){e.$().filter('[value="'+a+'"]').prop("checked",!0)})},d.equalTo=j,e.$().change(function(a){d.publishChange(a,this)}),e},m=function(a){var b={},c=x(a,b);return c.getType=function(){return"email"},c},n=function(c){var d={},e=b(c,d);return e.getType=function(){return"file"},e.get=function(){return h(e.$().val().split("\\"))},e.clear=function(){this.$().each(function(){a(this).wrap("<form>").closest("form").get(0).reset(),a(this).unwrap()})},e.$().change(function(a){d.publishChange(a,this)}),e},o=function(a){var b={},c=i(a,b);return c.getType=function(){return"hidden"},c.$().change(function(a){b.publishChange(a,this)}),c},r=function(c){var d={},e=b(c,d);return e.getType=function(){return"file[multiple]"},e.get=function(){var a,b=e.$().get(0).files||[],c=[];for(a=0;a<(b.length||0);a+=1)c.push(b[a].name);return c},e.clear=function(){this.$().each(function(){a(this).wrap("<form>").closest("form").get(0).reset(),a(this).unwrap()})},e.$().change(function(a){d.publishChange(a,this)}),e},s=function(a){var b={},d=i(a,b);return d.getType=function(){return"select[multiple]"},d.get=function(){return d.$().val()||[]},d.set=function(a){d.$().val(""===a?[]:c(a)?a:[a])},b.equalTo=j,d.$().change(function(a){b.publishChange(a,this)}),d},t=function(a){var b={},c=x(a,b);return c.getType=function(){return"password"},c},u=function(b){var c={},d=i(b,c);return d.getType=function(){return"radio"},d.get=function(){return d.$().filter(":checked").val()||null},d.set=function(b){b?d.$().filter('[value="'+b+'"]').prop("checked",!0):d.$().each(function(){a(this).prop("checked",!1)})},d.$().change(function(a){c.publishChange(a,this)}),d},v=function(a){var b={},c=i(a,b);return c.getType=function(){return"range"},c.$().change(function(a){b.publishChange(a,this)}),c},w=function(a){var b={},c=i(a,b);return c.getType=function(){return"select"},c.$().change(function(a){b.publishChange(a,this)}),c},x=function(a){var b={},c=i(a,b);return c.getType=function(){return"text"},c.$().on("change keyup keydown",function(a){b.publishChange(a,this)}),c},y=function(a){var b={},c=i(a,b);return c.getType=function(){return"textarea"},c.$().on("change keyup keydown",function(a){b.publishChange(a,this)}),c},z=function(a){var b={},c=x(a,b);return c.getType=function(){return"url"},c},A=function(b){var c={},f=b.$,h=b.constructorOverride||{button:k,text:x,url:z,email:m,password:t,range:v,textarea:y,select:w,"select[multiple]":s,radio:u,checkbox:l,file:n,"file[multiple]":r,hidden:o},i=function(b,e){var g=d(e)?e:f.find(e);g.each(function(){var d=a(this).attr("name");c[d]=h[b]({$:a(this)})})},j=function(b,i){var j=[],k=d(i)?i:f.find(i);d(i)?c[k.attr("name")]=h[b]({$:k}):(k.each(function(){e(j,a(this).attr("name"))===-1&&j.push(a(this).attr("name"))}),g(j,function(a){c[a]=h[b]({$:f.find('input[name="'+a+'"]')})}))};return f.is("input, select, textarea")?f.is('input[type="button"], button, input[type="submit"]')?i("button",f):f.is("textarea")?i("textarea",f):f.is('input[type="text"]')||f.is("input")&&!f.attr("type")?i("text",f):f.is('input[type="password"]')?i("password",f):f.is('input[type="email"]')?i("email",f):f.is('input[type="url"]')?i("url",f):f.is('input[type="range"]')?i("range",f):f.is("select")?f.is("[multiple]")?i("select[multiple]",f):i("select",f):f.is('input[type="file"]')?f.is("[multiple]")?i("file[multiple]",f):i("file",f):f.is('input[type="hidden"]')?i("hidden",f):f.is('input[type="radio"]')?j("radio",f):f.is('input[type="checkbox"]')?j("checkbox",f):i("text",f):(i("button",'input[type="button"], button, input[type="submit"]'),i("text",'input[type="text"]'),i("password",'input[type="password"]'),i("email",'input[type="email"]'),i("url",'input[type="url"]'),i("range",'input[type="range"]'),i("textarea","textarea"),i("select","select:not([multiple])"),i("select[multiple]","select[multiple]"),i("file",'input[type="file"]:not([multiple])'),i("file[multiple]",'input[type="file"][multiple]'),i("hidden",'input[type="hidden"]'),j("radio",'input[type="radio"]'),j("checkbox",'input[type="checkbox"]')),c};a.fn.inputVal=function(b){var c=a(this),d=A({$:c});return c.is("input, textarea, select")?"undefined"==typeof b?d[c.attr("name")].get():(d[c.attr("name")].set(b),c):"undefined"==typeof b?p(d,"get"):(g(b,function(a,b){d[b].set(a)}),c)},a.fn.inputOnChange=function(b){var c=a(this),d=A({$:c});return g(d,function(a){a.subscribe("change",function(a){b.call(a.domElement,a.e)})}),c},a.fn.inputDisable=function(){var b=a(this);return p(A({$:b}),"disable"),b},a.fn.inputEnable=function(){var b=a(this);return p(A({$:b}),"enable"),b},a.fn.inputClear=function(){var b=a(this);return p(A({$:b}),"clear"),b}}(jQuery),a.fn.repeaterVal=function(){var b=function(a){var b=[];return g(a,function(a,c){var d=[];"undefined"!==c&&(d.push(c.match(/^[^\[]*/)[0]),d=d.concat(m(c.match(/\[[^\]]*\]/g),function(a){return a.replace(/[\[\]]/g,"")})),b.push({val:a,key:d}))}),b},c=function(a){if(1===a.length&&(0===a[0].key.length||1===a[0].key.length&&!a[0].key[0]))return a[0].val;g(a,function(a){a.head=a.key.shift()});var b,d=function(){var b={};return g(a,function(a){b[a.head]||(b[a.head]=[]),b[a.head].push(a)}),b}();return/^[0-9]+$/.test(a[0].head)?(b=[],g(d,function(a){b.push(c(a))})):(b={},g(d,function(a,d){b[d]=c(a)})),b};return c(b(a(this).inputVal()))},a.fn.repeater=function(c){c=c||{};var d;return a(this).each(function(){var e=a(this),f=c.show||function(){a(this).show()},i=c.hide||function(a){a()},k=e.find("[data-repeater-list]").first(),l=function(b,c){return b.filter(function(){return!c||0===a(this).closest(n(c,"selector").join(",")).length})},p=function(){return l(k.find("[data-repeater-item]"),c.repeaters)},q=k.find("[data-repeater-item]").first().clone().hide(),r=l(l(a(this).find("[data-repeater-item]"),c.repeaters).first().find("[data-repeater-delete]"),c.repeaters);c.isFirstItemUndeletable&&r&&r.remove();var s=function(){var a=k.data("repeater-list");return c.$parent?c.$parent.data("item-name")+"["+a+"]":a},t=function(b){c.repeaters&&b.each(function(){var b=a(this);g(c.repeaters,function(a){b.find(a.selector).repeater(j(a,{$parent:b}))})})},u=function(a,b,c){a&&g(a,function(a){c.call(b.find(a.selector)[0],a)})},v=function(b,c,d){b.each(function(b){var e=a(this);e.data("item-name",c+"["+b+"]"),l(e.find("[name]"),d).each(function(){var f=a(this),g=f.attr("name").match(/\[[^\]]+\]/g),i=g?h(g).replace(/\[|\]/g,""):f.attr("name"),j=c+"["+b+"]["+i+"]"+(f.is(":checkbox")||f.attr("multiple")?"[]":"");f.attr("name",j),u(d,e,function(d){var e=a(this);v(l(e.find("[data-repeater-item]"),d.repeaters||[]),c+"["+b+"]["+e.find("[data-repeater-list]").first().data("repeater-list")+"]",d.repeaters)})})}),k.find("input[name][checked]").removeAttr("checked").prop("checked",!0)};v(p(),s(),c.repeaters),t(p()),c.initEmpty&&p().remove(),c.ready&&c.ready(function(){v(p(),s(),c.repeaters)});var w=function(){var d=function(e,f,h){if(f||c.defaultValues){var i={};l(e.find("[name]"),h).each(function(){var b=a(this).attr("name").match(/\[([^\]]*)(\]|\]\[\])$/)[1];i[b]=a(this).attr("name")}),e.inputVal(m(o(f||c.defaultValues,function(a,b){return i[b]}),b,function(a){return i[a]}))}u(h,e,function(b){var c=a(this);l(c.find("[data-repeater-item]"),b.repeaters).each(function(){var e=c.find("[data-repeater-list]").data("repeater-list");if(f&&f[e]){var h=a(this).clone();c.find("[data-repeater-item]").remove(),g(f[e],function(a){var e=h.clone();d(e,a,b.repeaters||[]),c.find("[data-repeater-list]").append(e)})}else d(a(this),b.defaultValues,b.repeaters||[])})})};return function(b,e){k.append(b),v(p(),s(),c.repeaters),b.find("[name]").each(function(){a(this).inputClear()}),d(b,e||c.defaultValues,c.repeaters)}}(),x=function(a){var b=q.clone();w(b,a),c.repeaters&&t(b),f.call(b.get(0))};d=function(a){p().remove(),g(a,x)},l(e.find("[data-repeater-create]"),c.repeaters).click(function(){x()}),k.on("click","[data-repeater-delete]",function(){var b=a(this).closest("[data-repeater-item]").get(0);i.call(b,function(){a(b).remove(),v(p(),s(),c.repeaters)})})}),this.setList=d,this}}(jQuery);
gdpr-framework.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Plugin Name: The GDPR Framework
5
+ * Plugin URI: https://codelight.eu/wordpress-gdpr-framework/
6
+ * Description: The GDPR Framework
7
+ * Version: 1.0.0
8
+ * Author: Codelight
9
+ * Author URI: https://codelight.eu/
10
+ * Text Domain: gdpr
11
+ * Domain Path: /languages
12
+ */
13
+
14
+ if (!defined('WPINC')) {
15
+ die;
16
+ }
17
+
18
+ /**
19
+ * Helper function for prettying up errors
20
+ *
21
+ * @param string $message
22
+ * @param string $subtitle
23
+ * @param string $title
24
+ */
25
+ $gdpr_error = function($message, $subtitle = '', $title = '') {
26
+ $title = $title ?: __('WordPress GDPR &rsaquo; Error', 'gdpr-admin');
27
+ $message = "<h1>{$title}<br><small>{$subtitle}</small></h1><p>{$message}</p>";
28
+ wp_die($message, $title);
29
+ };
30
+
31
+ /**
32
+ * Ensure compatible version of PHP is used
33
+ */
34
+ if (version_compare(phpversion(), '5.6.33', '<')) {
35
+ $gdpr_error(__('You must be using PHP 5.6.33 or greater.', 'gdpr-admin'), __('Invalid PHP version', 'gdpr-admin'));
36
+ }
37
+
38
+ /**
39
+ * Ensure compatible version of WordPress is used
40
+ */
41
+ if (version_compare(get_bloginfo('version'), '4.3', '<')) {
42
+ $gdpr_error(__('You must be using WordPress 4.3.0 or greater.', 'gdpr-admin'), __('Invalid WordPress version', 'gdpr-admin'));
43
+ }
44
+
45
+ /**
46
+ * Load dependencies
47
+ */
48
+ if (!class_exists('\Codelight\GDPR\Container')) {
49
+
50
+ if (!file_exists($composer = __DIR__ . '/vendor/autoload.php')) {
51
+ $gdpr_error(
52
+ __('You appear to be running a development version of GDPR. You must run <code>composer install</code> from the plugin directory.', 'gdpr-admin'),
53
+ __('Autoloader not found.', 'gdpr-admin')
54
+ );
55
+ }
56
+ require_once $composer;
57
+ }
58
+
59
+ /**
60
+ * Set up config object, store plugin URL and path there
61
+ * along with various other items
62
+ */
63
+ \Codelight\GDPR\Container::getInstance()
64
+ ->bindIf('config', function () {
65
+ return new \Codelight\GDPR\Config([
66
+ 'plugin' => [
67
+ 'url' => plugin_dir_url(__FILE__),
68
+ 'path' => plugin_dir_path(__FILE__),
69
+ 'template_path' => plugin_dir_path(__FILE__) . 'views/',
70
+ ],
71
+ 'help' => [
72
+ 'url' => 'https://codelight.eu/wordpress-gdpr-framework/'
73
+ ]
74
+ ]);
75
+ }, true);
76
+
77
+ /**
78
+ * Set up the application container
79
+ *
80
+ * @param string $abstract
81
+ * @param array $parameters
82
+ * @param Codelight\GDPR\Container $container
83
+ * @return Codelight\GDPR\Container|mixed
84
+ */
85
+ function gdpr($abstract = null, $parameters = [], Codelight\GDPR\Container $container = null)
86
+ {
87
+ $container = $container ?: Codelight\GDPR\Container::getInstance();
88
+
89
+ if (!$abstract) {
90
+ return $container;
91
+ }
92
+ return $container->bound($abstract)
93
+ ? $container->makeWith($abstract, $parameters)
94
+ : $container->makeWith("gdpr.{$abstract}", $parameters);
95
+ }
96
+
97
+ /**
98
+ * Start the plugin on plugins_loaded at priority 0.
99
+ */
100
+ add_action('plugins_loaded', function() use ($gdpr_error){
101
+ new \Codelight\GDPR\Setup();
102
+ }, 0);
103
+
104
+ /**
105
+ * Install the database table and custom role
106
+ */
107
+ register_activation_hook(__FILE__, function () {
108
+ $model = new \Codelight\GDPR\Components\Consent\UserConsentModel();
109
+ $model->createTable();
110
+
111
+ if (apply_filters('gdpr/data-subject/anonymize/change_role', true) && !get_role('anonymous')) {
112
+
113
+ add_role(
114
+ 'anonymous',
115
+ __('Anonymous', 'gdpr-admin'),
116
+ []
117
+ );
118
+ }
119
+ });
license.txt ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ This program is free software: you can redistribute it and/or modify
2
+ it under the terms of the GNU General Public License as published by
3
+ the Free Software Foundation, either version 3 of the License, or
4
+ (at your option) any later version.
5
+
6
+ This program is distributed in the hope that it will be useful,
7
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
+ GNU General Public License for more details.
10
+
11
+ You should have received a copy of the GNU General Public License
12
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
13
+
14
+
15
+ GNU GENERAL PUBLIC LICENSE
16
+ Version 3, 29 June 2007
17
+
18
+ Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/>
19
+
20
+ Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
21
+
22
+ Preamble
23
+ The GNU General Public License is a free, copyleft license for software and other kinds of works.
24
+
25
+ The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.
26
+
27
+ When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.
28
+
29
+ To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.
30
+
31
+ For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
32
+
33
+ Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.
34
+
35
+ For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.
36
+
37
+ Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.
38
+
39
+ Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.
40
+
41
+ The precise terms and conditions for copying, distribution and modification follow.
42
+
43
+ TERMS AND CONDITIONS
44
+ 0. Definitions.
45
+ “This License” refers to version 3 of the GNU General Public License.
46
+
47
+ “Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.
48
+
49
+ “The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations.
50
+
51
+ To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.
52
+
53
+ A “covered work” means either the unmodified Program or a work based on the Program.
54
+
55
+ To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.
56
+
57
+ To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.
58
+
59
+ An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.
60
+
61
+ 1. Source Code.
62
+ The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work.
63
+
64
+ A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.
65
+
66
+ The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.
67
+
68
+ The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.
69
+
70
+ The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
71
+
72
+ The Corresponding Source for a work in source code form is that same work.
73
+
74
+ 2. Basic Permissions.
75
+ All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.
76
+
77
+ You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.
78
+
79
+ Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
80
+
81
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
82
+ No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.
83
+
84
+ When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.
85
+
86
+ 4. Conveying Verbatim Copies.
87
+ You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.
88
+
89
+ You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.
90
+
91
+ 5. Conveying Modified Source Versions.
92
+ You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:
93
+
94
+ a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
95
+ b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”.
96
+ c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
97
+ d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
98
+ A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.
99
+
100
+ 6. Conveying Non-Source Forms.
101
+ You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:
102
+
103
+ a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
104
+ b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
105
+ c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
106
+ d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
107
+ e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
108
+ A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.
109
+
110
+ A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.
111
+
112
+ “Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.
113
+
114
+ If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).
115
+
116
+ The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.
117
+
118
+ Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.
119
+
120
+ 7. Additional Terms.
121
+ “Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.
122
+
123
+ When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.
124
+
125
+ Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:
126
+
127
+ a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
128
+ b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
129
+ c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
130
+ d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
131
+ e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
132
+ f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.
133
+ All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.
134
+
135
+ If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.
136
+
137
+ Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.
138
+
139
+ 8. Termination.
140
+ You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).
141
+
142
+ However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
143
+
144
+ Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
145
+
146
+ Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.
147
+
148
+ 9. Acceptance Not Required for Having Copies.
149
+ You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.
150
+
151
+ 10. Automatic Licensing of Downstream Recipients.
152
+ Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.
153
+
154
+ An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.
155
+
156
+ You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.
157
+
158
+ 11. Patents.
159
+ A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”.
160
+
161
+ A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.
162
+
163
+ Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.
164
+
165
+ In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.
166
+
167
+ If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.
168
+
169
+ If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.
170
+
171
+ A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.
172
+
173
+ Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.
174
+
175
+ 12. No Surrender of Others' Freedom.
176
+ If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.
177
+
178
+ 13. Use with the GNU Affero General Public License.
179
+ Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.
180
+
181
+ 14. Revised Versions of this License.
182
+ The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
183
+
184
+ Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.
185
+
186
+ If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.
187
+
188
+ Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.
189
+
190
+ 15. Disclaimer of Warranty.
191
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
192
+
193
+ 16. Limitation of Liability.
194
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
195
+
196
+ 17. Interpretation of Sections 15 and 16.
197
+ If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.
198
+
199
+ END OF TERMS AND CONDITIONS
readme.txt ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 
2
+ === The GDPR Framework ===
3
+ Contributors: codelight
4
+ Tags: gdpr
5
+ Requires at least: 4.3
6
+ Tested up to: 4.9.4
7
+ Requires PHP: 5.6.33
8
+ License: GPLv3
9
+ License URI: https://www.gnu.org/licenses/gpl-3.0.en.html
10
+
11
+ The easiest way to make your website GDPR-compliant. Fully documented, extendable and developer-friendly.
12
+
13
+ == Description ==
14
+
15
+ The easiest way to make your WordPress site GDPR compliant!
16
+
17
+ GDPR is a whopping 88 pages of legal text. Becoming compliant takes a lot more than just adding a couple of checkboxes to your forms! But worry not, we’ve got it covered. With help from [Triniti](https://triniti.eu), one of the top business and IT law firms in Europe, we’ve put together this plugin and written a thorough guide for making WordPress sites compliant with minimal effort.
18
+
19
+ You don't need to drown your customers in pointless acceptance checkboxes if you know what you're doing!
20
+
21
+ ### Documentation
22
+ Full documentation: [The WordPress Site Owner's Guide to GDPR](https://codelight.eu/wordpress-gdpr-framework/wordpress-site-owners-guide-to-gdpr/)
23
+ For developers: [Developer Docs](https://codelight.eu/wordpress-gdpr-framework/developer-docs/)
24
+
25
+ ### Features
26
+ The GDPR Framework covers all the base requirements for making your WordPress site GDPR-compliant.
27
+
28
+ &#9745; Allow both users and visitors without an account to view, export and delete their personal data;
29
+ &#9745; Configure the plugin to delete or anonymize personal data automatically or send a notification and allow admins to do it manually;
30
+ &#9745; Track, manage and withdraw consent;
31
+ &#9745; Generate a GDPR-compatible Privacy Policy template for your site;
32
+ &#9745; Comes with a helpful installation wizard to get you started quickly;
33
+ &#9745; Fully documented;
34
+ &#9745; Developer-friendly. Everything can be extended, every feature and template can be overridden.
35
+
36
+ We're just getting started. There's lots more to come!
37
+
38
+ ### Plugin support:
39
+ The GDPR Framework currently works with the following plugins
40
+ &#9745; Contact Form 7
41
+ &#9745; WPML
42
+
43
+ Coming in April 2018:
44
+ &#9744; Gravity Forms (Release: 17.04)
45
+ &#9744; WooCommerce (Release: 19.04)
46
+ &#9744; Easy Digital Downloads (Release: 23.04)
47
+ &#9744; Ninja Forms (Release: 25.04)
48
+
49
+ Coming soon:
50
+ &#9744; other form builders
51
+ &#9744; Contact Form 7 Flamingo
52
+ &#9744; WP Migrate DB
53
+ &#9744; WP Rocket
54
+
55
+
56
+ All free and open-source.
57
+
58
+ We're happy to add support for other major plugins as well. If you have a request, get in touch!
src/Admin/AdminError.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace Codelight\GDPR\Admin;
5
+
6
+
7
+ class AdminError extends AdminNotice
8
+ {
9
+ public function render()
10
+ {
11
+ if (!$this->template) {
12
+ trigger_error('Template not set for admin notice!', E_USER_ERROR);
13
+ }
14
+
15
+ echo gdpr('view')->render($this->template, $this->data);
16
+ }
17
+ }
src/Admin/AdminHelper.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Admin;
4
+
5
+ class AdminHelper
6
+ {
7
+ public function __construct()
8
+ {
9
+ $this->toolsHelper();
10
+ $this->autoinstallHelper();
11
+ $this->policyHelper();
12
+ }
13
+
14
+ protected function toolsHelper()
15
+ {
16
+ $toolsPage = gdpr('options')->get('tools_page');
17
+
18
+ // Display the notice only on Tools page
19
+ if (!$toolsPage || !isset($_GET['post']) || $_GET['post'] !== $toolsPage) {
20
+ return;
21
+ }
22
+
23
+ $post = get_post($toolsPage);
24
+ $helpUrl = gdpr('helpers')->docs();
25
+
26
+ if (!stristr($post->post_content, '[gdpr_privacy_tools]')) {
27
+ gdpr('admin-notice')->add('admin/notices/helper-tools', compact('helpUrl'));
28
+ }
29
+ }
30
+
31
+ protected function autoinstallHelper()
32
+ {
33
+ if (!isset($_GET['gdpr-notice']) || empty($_GET['gdpr-notice']) || 'autoinstall' !== $_GET['gdpr-notice']) {
34
+ return;
35
+ }
36
+
37
+ $helpUrl = gdpr('helpers')->docs();
38
+ gdpr('admin-notice')->add('admin/notices/helper-autoinstall', compact('helpUrl'));
39
+ }
40
+
41
+ protected function policyHelper()
42
+ {
43
+ $policyPage = gdpr('options')->get('policy_page');
44
+
45
+ // Display the notice only on Policy page
46
+ if (!$policyPage || !isset($_GET['post']) || $_GET['post'] !== $policyPage) {
47
+ return;
48
+ }
49
+
50
+ $post = get_post($policyPage);
51
+ $helpUrl = gdpr('helpers')->docs();
52
+
53
+ if (stristr($post->post_content, '[TODO]')) {
54
+ gdpr('admin-notice')->add('admin/notices/helper-policy', compact('helpUrl'));
55
+ }
56
+ }
57
+ }
src/Admin/AdminNotice.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Admin;
4
+
5
+ class AdminNotice
6
+ {
7
+ protected $template;
8
+
9
+ protected $data;
10
+
11
+ /**
12
+ * todo: replace with a proper factory pattern via gdpr()?
13
+ *
14
+ * AdminNotice constructor.
15
+ */
16
+ public function __construct()
17
+ {
18
+ if (did_action('admin_notices')) {
19
+ trigger_error('AdminNotice class called incorrectly - admin_notices action has already ran!', E_USER_ERROR);
20
+ }
21
+
22
+ add_action('admin_notices', [$this, 'render'], 9999);
23
+ }
24
+
25
+ public function add($template, $data = [])
26
+ {
27
+ $this->template = $template;
28
+ $this->data = $data;
29
+ }
30
+
31
+ public function render()
32
+ {
33
+ if (!$this->template) {
34
+ trigger_error('Template not set for admin notice!', E_USER_ERROR);
35
+ }
36
+
37
+ echo gdpr('view')->render('admin/notices/header');
38
+ echo gdpr('view')->render($this->template, $this->data);
39
+ echo gdpr('view')->render('admin/notices/footer');
40
+ }
41
+ }
src/Admin/AdminTab.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Admin;
4
+
5
+ /**
6
+ * Base class for admin tabs. Extend this.
7
+ *
8
+ * Class AdminTab
9
+ *
10
+ * @package Codelight\GDPR\Admin
11
+ */
12
+ abstract class AdminTab implements AdminTabInterface
13
+ {
14
+ /* @var string */
15
+ protected $slug;
16
+
17
+ /* @var string */
18
+ protected $title;
19
+
20
+ /**
21
+ * @return string
22
+ */
23
+ public function getSlug()
24
+ {
25
+ return $this->slug;
26
+ }
27
+
28
+ /**
29
+ * @return string
30
+ */
31
+ public function getTitle()
32
+ {
33
+ return $this->title;
34
+ }
35
+
36
+ /**
37
+ * @return string
38
+ */
39
+ public function getOptionsGroupName()
40
+ {
41
+ return 'gdpr_' . $this->getSlug();
42
+ }
43
+
44
+ /**
45
+ * Register a setting on the admin page
46
+ *
47
+ * @param $optionName
48
+ * @param string $args
49
+ */
50
+ public function registerSetting($optionName, $args = [])
51
+ {
52
+ register_setting($this->getOptionsGroupName(), $optionName, $args);
53
+ }
54
+
55
+ /**
56
+ * Register a section on the admin page
57
+ *
58
+ * @param $name
59
+ * @param $callback
60
+ */
61
+ public function registerSettingSection($id, $title, $callback = null)
62
+ {
63
+ add_settings_section(
64
+ $id,
65
+ $title,
66
+ $callback,
67
+ $this->getOptionsGroupName()
68
+ );
69
+ }
70
+
71
+ /**
72
+ * Register a setting field on the admin page
73
+ *
74
+ * @param $id
75
+ * @param $title
76
+ * @param $callback
77
+ */
78
+ public function registerSettingField($id, $title, $callback = null, $section = '', $args = [])
79
+ {
80
+ add_settings_field(
81
+ $id,
82
+ $title,
83
+ $callback,
84
+ $this->getOptionsGroupName(),
85
+ $section,
86
+ $args
87
+ );
88
+ }
89
+
90
+ /**
91
+ * Render the contents including settings fields, sections and submit button.
92
+ * Trigger hooks for rendering content before and after the settings fields.
93
+ *
94
+ * @return string
95
+ */
96
+ public function renderContents()
97
+ {
98
+ ob_start();
99
+
100
+ do_action("gdpr/tabs/{$this->getSlug()}/before", $this);
101
+ settings_fields($this->getOptionsGroupName());
102
+ do_settings_sections($this->getOptionsGroupName());
103
+ do_action("gdpr/tabs/{$this->getSlug()}/after", $this);
104
+
105
+ $this->renderSubmitButton();
106
+
107
+ return ob_get_clean();
108
+ }
109
+
110
+ /**
111
+ * Render WP's default submit button
112
+ */
113
+ public function renderSubmitButton()
114
+ {
115
+ submit_button(__('Save', 'gdpr-admin'));
116
+ }
117
+
118
+ /**
119
+ * Enqueue scripts, run the child class init function, trigger action for adding custom stuff
120
+ */
121
+ public function setup()
122
+ {
123
+ // Automatically run the 'enqueue' method if it exists
124
+ if (method_exists($this, 'enqueue')) {
125
+ add_action('admin_enqueue_scripts', [$this, 'enqueue']);
126
+ }
127
+
128
+ $this->init();
129
+
130
+ // This hook can be used for registering custom settings
131
+ do_action("gdpr/tabs/{$this->getSlug()}/init", $this);
132
+
133
+ // Render the admin notices
134
+ add_action('admin_notices', [$this, 'renderAdminNotices']);
135
+ }
136
+
137
+ /**
138
+ * Render success notices via admin_notice action
139
+ */
140
+ public function renderAdminNotices()
141
+ {
142
+ if ('tools_page_privacy' !== get_current_screen()->base) {
143
+ return;
144
+ }
145
+
146
+ if (!isset($_REQUEST['gdpr_notice'])) {
147
+ return;
148
+ }
149
+
150
+ if ('policy_generated' === $_REQUEST['gdpr_notice']) {
151
+ $message = __('Policy generated!', 'gdpr-admin');
152
+ $class = 'notice notice-success';
153
+ }
154
+
155
+ echo gdpr('view')->render('admin/notice', compact('message', 'class'));
156
+ }
157
+ }
src/Admin/AdminTabGeneral.php ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Admin;
4
+
5
+ class AdminTabGeneral extends AdminTab
6
+ {
7
+ protected $slug = 'general';
8
+
9
+ public function __construct()
10
+ {
11
+ $this->title = __('General', 'gdpr-admin');
12
+
13
+ $this->registerSetting('gdpr_enable');
14
+
15
+ $this->registerSetting('gdpr_tools_page');
16
+ $this->registerSetting('gdpr_policy_page');
17
+ $this->registerSetting('gdpr_terms_page');
18
+
19
+ $this->registerSetting('gdpr_export_action');
20
+ $this->registerSetting('gdpr_export_action_email');
21
+
22
+ $this->registerSetting('gdpr_delete_action');
23
+ $this->registerSetting('gdpr_delete_action_reassign');
24
+ $this->registerSetting('gdpr_delete_action_reassign_user');
25
+ $this->registerSetting('gdpr_delete_action_email');
26
+
27
+ $this->registerSetting('gdpr_enable_theme_compatibility');
28
+ }
29
+
30
+ public function init()
31
+ {
32
+ /**
33
+ * General
34
+ */
35
+ $this->registerSettingSection(
36
+ 'gdpr_section_general',
37
+ __('General Settings', 'gdpr-admin')
38
+ );
39
+
40
+ $this->registerSettingField(
41
+ 'gdpr_enable',
42
+ __('Enable Privacy Tools', 'gdpr-admin'),
43
+ [$this, 'renderEnableCheckbox'],
44
+ 'gdpr_section_general'
45
+ );
46
+
47
+ /**
48
+ * GDPR system pages
49
+ */
50
+ $this->registerSettingSection(
51
+ 'gdpr_section_pages',
52
+ __('Pages', 'gdpr-admin')
53
+ );
54
+
55
+ $this->registerSettingField(
56
+ 'gdpr_tools_page',
57
+ __('Privacy Tools Page', 'gdpr-admin') . '*',
58
+ [$this, 'renderPrivacyToolsPageSelector'],
59
+ 'gdpr_section_pages'
60
+ );
61
+
62
+ $this->registerSettingField(
63
+ 'gdpr_policy_page',
64
+ __('Privacy Policy Page', 'gdpr-admin') . '*',
65
+ [$this, 'renderPolicyPageSelector'],
66
+ 'gdpr_section_pages'
67
+ );
68
+
69
+ $this->registerSettingField(
70
+ 'gdpr_terms_page',
71
+ __('Terms & Conditions Page', 'gdpr-admin'),
72
+ [$this, 'renderTermsPageSelector'],
73
+ 'gdpr_section_pages'
74
+ );
75
+
76
+ /**
77
+ * View & Export
78
+ */
79
+ $this->registerSettingSection(
80
+ 'gdpr_section_export',
81
+ __('View & Export Data', 'gdpr-admin')
82
+ );
83
+
84
+ $this->registerSettingField(
85
+ 'gdpr_export_action',
86
+ __('Export action', 'gdpr-admin'),
87
+ [$this, 'renderExportActionSelector'],
88
+ 'gdpr_section_export'
89
+ );
90
+
91
+ $this->registerSettingField(
92
+ 'gdpr_export_action_email',
93
+ __('Email to notify', 'gdpr-admin'),
94
+ [$this, 'renderExportActionEmail'],
95
+ 'gdpr_section_export',
96
+ ['class' => 'js-gdpr-export-action-email hidden']
97
+ );
98
+
99
+ /**
100
+ * Delete data
101
+ */
102
+ $this->registerSettingSection(
103
+ 'gdpr_section_delete',
104
+ __('Delete & Anonymize Data', 'gdpr-admin')
105
+ );
106
+
107
+ $this->registerSettingField(
108
+ 'gdpr_delete_action',
109
+ __('Delete action', 'gdpr-admin'),
110
+ [$this, 'renderDeleteActionSelector'],
111
+ 'gdpr_section_delete'
112
+ );
113
+
114
+ $this->registerSettingField(
115
+ 'gdpr_delete_action_reassign',
116
+ __('Delete or reassign content?', 'gdpr-admin'),
117
+ [$this, 'renderDeleteActionReassign'],
118
+ 'gdpr_section_delete',
119
+ ['class' => 'js-gdpr-delete-action-reassign hidden']
120
+ );
121
+
122
+ $this->registerSettingField(
123
+ 'gdpr_delete_action_reassign_user',
124
+ __('Reassign content to', 'gdpr-admin'),
125
+ [$this, 'renderDeleteActionReassignUser'],
126
+ 'gdpr_section_delete',
127
+ ['class' => 'js-gdpr-delete-action-reassign-user hidden']
128
+ );
129
+
130
+ $this->registerSettingField(
131
+ 'gdpr_delete_action_email',
132
+ __('Email to notify', 'gdpr-admin'),
133
+ [$this, 'renderDeleteActionEmail'],
134
+ 'gdpr_section_delete',
135
+ ['class' => 'js-gdpr-delete-action-email hidden']
136
+ );
137
+
138
+ if (gdpr('themes')->isCurrentThemeSupported()) {
139
+
140
+ /**
141
+ * General settings
142
+ */
143
+ $this->registerSettingSection(
144
+ 'gdpr_section_compatibility',
145
+ __('Compatibility', 'gdpr-admin')
146
+ );
147
+
148
+ $this->registerSettingField(
149
+ 'gdpr_enable_theme_compatibility',
150
+ __('Enable automatic theme compatibility', 'gdpr-admin'),
151
+ [$this, 'renderThemeCompatibilitySelector'],
152
+ 'gdpr_section_compatibility'
153
+ );
154
+ }
155
+ }
156
+
157
+ public function renderEnableCheckbox()
158
+ {
159
+ $enabled = gdpr('options')->get('enable');
160
+ echo gdpr('view')->render('admin/general/enable', compact('enabled'));
161
+ }
162
+
163
+ public function renderPrivacyToolsPageSelector()
164
+ {
165
+ wp_dropdown_pages([
166
+ 'name' => 'gdpr_tools_page',
167
+ 'show_option_none' => __('&mdash; Select &mdash;'),
168
+ 'option_none_value' => '0',
169
+ 'selected' => gdpr('options')->get('tools_page'),
170
+ 'class' => 'js-gdpr-select2 gdpr-select',
171
+ 'post_status' => 'publish,draft',
172
+ ]);
173
+ echo gdpr('view')->render('admin/general/description-data-page');
174
+ }
175
+
176
+ /**
177
+ * Render the GDPR policy page selector dropdown
178
+ */
179
+ public function renderPolicyPageSelector()
180
+ {
181
+ wp_dropdown_pages([
182
+ 'name' => 'gdpr_policy_page',
183
+ 'show_option_none' => __('&mdash; Select &mdash;'),
184
+ 'option_none_value' => '0',
185
+ 'selected' => gdpr('options')->get('policy_page'),
186
+ 'class' => 'js-gdpr-select2 gdpr-select',
187
+ 'post_status' => 'publish,draft',
188
+ ]);
189
+ echo gdpr('view')->render('admin/privacy-policy/description-policy-page');
190
+ }
191
+
192
+ public function renderTermsPageSelector()
193
+ {
194
+ wp_dropdown_pages([
195
+ 'name' => 'gdpr_terms_page',
196
+ 'show_option_none' => __('&mdash; Select &mdash;'),
197
+ 'option_none_value' => '0',
198
+ 'selected' => gdpr('options')->get('terms_page'),
199
+ 'class' => 'js-gdpr-select2 gdpr-select',
200
+ 'post_status' => 'publish,draft',
201
+ ]);
202
+ echo gdpr('view')->render('admin/general/description-terms-page');
203
+ }
204
+
205
+ public function renderExportActionSelector()
206
+ {
207
+ $exportAction = gdpr('options')->get('export_action');
208
+ echo gdpr('view')->render('admin/general/export-action', compact('exportAction'));
209
+ echo gdpr('view')->render('admin/general/description-export-action');
210
+ }
211
+
212
+ public function renderExportActionEmail()
213
+ {
214
+ $exportActionEmail = gdpr('options')->get('export_action_email');
215
+ echo gdpr('view')->render('admin/general/export-action-email', compact('exportActionEmail'));
216
+ }
217
+
218
+ public function renderDeleteActionSelector()
219
+ {
220
+ $deleteAction = gdpr('options')->get('delete_action');
221
+ echo gdpr('view')->render('admin/general/delete-action', compact('deleteAction'));
222
+ echo gdpr('view')->render('admin/general/description-delete-action');
223
+ }
224
+
225
+ public function renderDeleteActionReassign()
226
+ {
227
+ $reassign = gdpr('options')->get('delete_action_reassign');
228
+ echo gdpr('view')->render('admin/general/delete-action-reassign', compact('reassign'));
229
+ }
230
+
231
+ public function renderDeleteActionReassignUser()
232
+ {
233
+ wp_dropdown_users([
234
+ 'name' => 'gdpr_delete_action_reassign_user',
235
+ 'show_option_none' => __('&mdash; Select &mdash;'),
236
+ 'option_none_value' => '0',
237
+ 'selected' => gdpr('options')->get('delete_action_reassign_user'),
238
+ 'class' => 'js-gdpr-select2 gdpr-select',
239
+ 'role__in' => apply_filters('gdpr/options/reassign/roles', ['administrator', 'editor']),
240
+ ]);
241
+ }
242
+
243
+ public function renderDeleteActionEmail()
244
+ {
245
+ $deleteActionEmail = gdpr('options')->get('delete_action_email');
246
+ echo gdpr('view')->render('admin/general/delete-action-email', compact('deleteActionEmail'));
247
+ }
248
+
249
+ public function renderThemeCompatibilitySelector()
250
+ {
251
+ $enabled = gdpr('options')->get('enable_theme_compatibility');
252
+ echo gdpr('view')->render('admin/general/theme-compatibility', compact('enabled'));
253
+ }
254
+ }
src/Admin/AdminTabInterface.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Admin;
4
+
5
+ interface AdminTabInterface
6
+ {
7
+ /**
8
+ * @return string
9
+ */
10
+ public function getSlug();
11
+
12
+ /**
13
+ * @return string
14
+ */
15
+ public function getTitle();
16
+
17
+ /**
18
+ * Use this function to register settings and fields
19
+ *
20
+ * @return void
21
+ */
22
+ public function init();
23
+
24
+ /**
25
+ * Wrapper around init() function.
26
+ * You probably don't need to override this.
27
+ *
28
+ * @return void
29
+ */
30
+ public function setup();
31
+
32
+ /**
33
+ * Wrapper around render() function.
34
+ * Automatically calls settings_fields(), do_settings_sections() and submit_button().
35
+ * Override this if you don't want the functions to be called.
36
+ *
37
+ * @return string
38
+ */
39
+ public function renderContents();
40
+ }
src/Admin/Modal.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Admin;
4
+
5
+ class Modal
6
+ {
7
+ protected $id;
8
+
9
+ protected $template;
10
+
11
+ protected $data;
12
+
13
+ /**
14
+ * todo: replace with a proper factory pattern via gdpr()?
15
+ *
16
+ * AdminNotice constructor.
17
+ */
18
+ public function __construct()
19
+ {
20
+ add_action('admin_footer', [$this, 'render']);
21
+ }
22
+
23
+ public function add($id, $template, $data = [])
24
+ {
25
+ $this->id = $id;
26
+ $this->template = $template;
27
+ $this->data = $data;
28
+ $this->data['id'] = $this->id;
29
+ }
30
+
31
+ public function render()
32
+ {
33
+ if (!$this->template) {
34
+ trigger_error('Template not set for admin notice!', E_USER_ERROR);
35
+ }
36
+
37
+ echo gdpr('view')->render('admin/modals/header', $this->data);
38
+ echo gdpr('view')->render($this->template, $this->data);
39
+ echo gdpr('view')->render('admin/modals/footer', $this->data);
40
+ }
41
+ }
src/Admin/WordpressAdmin.php ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Admin;
4
+
5
+ /**
6
+ * Handles general admin functionality
7
+ *
8
+ * Class WordpressAdmin
9
+ *
10
+ * @package Codelight\GDPR\Admin
11
+ */
12
+ class WordpressAdmin
13
+ {
14
+ public function __construct(WordpressAdminPage $adminPage)
15
+ {
16
+ $this->adminPage = $adminPage;
17
+
18
+ // Register the AdminTabGeneral class in our container
19
+ gdpr()->bind(AdminTabGeneral::class);
20
+
21
+ // Allow turning off helpers
22
+ if (apply_filters('gdpr/admin/helpers/enabled', true)) {
23
+ gdpr()->make(AdminHelper::class);
24
+ }
25
+
26
+ $this->setup();
27
+
28
+ }
29
+
30
+ /**
31
+ * Set up hooks
32
+ */
33
+ protected function setup()
34
+ {
35
+ // Register the main GDPR options page
36
+ add_action('admin_menu', [$this, 'registerGDPROptionsPage']);
37
+
38
+ // Register General admin tab
39
+ add_filter('gdpr/admin/tabs', [$this, 'registerAdminTabGeneral'], 0);
40
+
41
+ // Enqueue assets
42
+ add_action('admin_enqueue_scripts', [$this, 'enqueue']);
43
+
44
+ // Register post states
45
+ add_filter('display_post_states', [$this, 'registerPostStates'], 10, 2);
46
+
47
+ // Show help notice
48
+ add_action('current_screen', [$this, 'maybeShowHelpNotice'], 999);
49
+ }
50
+
51
+
52
+ public function maybeShowHelpNotice()
53
+ {
54
+ if ('tools_page_privacy' === get_current_screen()->base) {
55
+ gdpr('admin-notice')->add('admin/notices/help');
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Register the GDPR options page in WP admin
61
+ */
62
+ public function registerGDPROptionsPage()
63
+ {
64
+ add_management_page(
65
+ __('Privacy & GDPR Settings', 'gdpr-admin'),
66
+ __('Privacy', 'gdpr-admin'),
67
+ 'manage_options',
68
+ 'privacy',
69
+ [$this->adminPage, 'renderPage']
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Register General admin tab
75
+ *
76
+ * @param $tabs
77
+ * @return array
78
+ */
79
+ public function registerAdminTabGeneral($tabs)
80
+ {
81
+ $tabs['general'] = gdpr(AdminTabGeneral::class);
82
+
83
+ return $tabs;
84
+ }
85
+
86
+ /**
87
+ * Enqueue all admin scripts and styles
88
+ */
89
+ public function enqueue()
90
+ {
91
+ /**
92
+ * General admin styles
93
+ */
94
+ wp_enqueue_style(
95
+ 'gdpr-admin',
96
+ gdpr('config')->get('plugin.url') . 'assets/gdpr-admin.css'
97
+ );
98
+
99
+
100
+
101
+ /**
102
+ * jQuery UI dialog for modals
103
+ */
104
+ wp_enqueue_style('wp-jquery-ui-dialog');
105
+ wp_enqueue_script(
106
+ 'gdpr-admin',
107
+ gdpr('config')->get('plugin.url') . 'assets/gdpr-admin.js',
108
+ ['jquery-ui-dialog']
109
+ );
110
+
111
+ /**
112
+ * jQuery Repeater
113
+ */
114
+ wp_enqueue_script(
115
+ 'jquery-repeater',
116
+ gdpr('config')->get('plugin.url') . 'assets/jquery.repeater.min.js',
117
+ ['jquery']
118
+ );
119
+
120
+ /**
121
+ * Select2
122
+ */
123
+ wp_enqueue_style(
124
+ 'select2css',
125
+ '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.min.css'
126
+ );
127
+
128
+ wp_enqueue_script(
129
+ 'select2',
130
+ '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.min.js',
131
+ ['jquery']
132
+ );
133
+
134
+ wp_enqueue_script(
135
+ 'conditional-show',
136
+ gdpr('config')->get('plugin.url') . 'assets/conditional-show.js',
137
+ ['jquery']
138
+ );
139
+ }
140
+
141
+ /**
142
+ * Add a new Post State for our super important system pages
143
+ */
144
+ public function registerPostStates($postStates, $post)
145
+ {
146
+ if (gdpr('options')->get('policy_page') == $post->ID) {
147
+ $postStates['gdpr_policy_page'] = __('Privacy Policy Page', 'gdpr-admin');
148
+ }
149
+
150
+ if (gdpr('options')->get('tools_page') == $post->ID) {
151
+ $postStates['gdpr_tools_page'] = __('Privacy Tools Page', 'gdpr-admin');
152
+ }
153
+
154
+ return $postStates;
155
+ }
156
+ }
src/Admin/WordpressAdminPage.php ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Admin;
4
+
5
+ /**
6
+ * Handle registering and rendering the GDPR admin page contents
7
+ *
8
+ * Class WordpressAdminPage
9
+ *
10
+ * @package Codelight\GDPR\Admin
11
+ */
12
+ class WordpressAdminPage
13
+ {
14
+ protected $slug = 'gdpr';
15
+
16
+ protected $tabs = [];
17
+
18
+ public function __construct()
19
+ {
20
+ $this->setup();
21
+ }
22
+
23
+ protected function setup()
24
+ {
25
+ // Register the tabs
26
+ add_action('admin_init', [$this, 'registerTabs']);
27
+
28
+ // todo
29
+ //if (gdpr('options')->get('plugin_disclaimer_accepted')) {
30
+ // Initialize the active tab
31
+ add_action('admin_init', [$this, 'initActiveTab']);
32
+ //}
33
+
34
+ // todo
35
+ // gdpr('admin-modal')->add('gdpr-test', 'admin/modals/test', ['title' => 'Test modal']);
36
+ }
37
+
38
+ /**
39
+ * Render the main GDPR options page
40
+ */
41
+ public function renderPage()
42
+ {
43
+ $tabs = $this->getNavigationData();
44
+ $currentTabContents = $this->getActiveTab()->renderContents();
45
+ $signature = apply_filters('gdpr/admin/show_signature', true);
46
+ echo gdpr('view')->render('admin/settings-page', compact('tabs', 'currentTabContents', 'signature'));
47
+ }
48
+
49
+ /**
50
+ * Allow modules to add tabs
51
+ */
52
+ public function registerTabs()
53
+ {
54
+ $this->tabs = apply_filters('gdpr/admin/tabs', []);
55
+ }
56
+
57
+ /**
58
+ * Get the active tab or the first tab if none are active
59
+ *
60
+ * @return AdminTabInterface
61
+ */
62
+ public function getActiveTab()
63
+ {
64
+ foreach ($this->tabs as $tab) {
65
+ if (isset($_GET['gdpr-tab']) && $_GET['gdpr-tab'] === $tab->getSlug()) {
66
+ return $tab;
67
+ }
68
+ }
69
+
70
+ return reset($this->tabs);
71
+ }
72
+
73
+ /**
74
+ * Check if the given tab is active
75
+ *
76
+ * @param $slug
77
+ * @return bool
78
+ */
79
+ public function isTabActive($slug)
80
+ {
81
+ $activeTab = $this->getActiveTab();
82
+ if ($activeTab->getSlug() === $slug) {
83
+ return true;
84
+ }
85
+
86
+ // Hacky: if no tab set, the first tab is active
87
+ if (!isset($_GET['gdpr-tab'])) {
88
+ $firstTab = reset($this->tabs);
89
+ if ($firstTab->getSlug() === $slug) {
90
+ return true;
91
+ }
92
+ }
93
+
94
+ return false;
95
+ }
96
+
97
+ /**
98
+ * Initialize the active tab
99
+ */
100
+ public function initActiveTab()
101
+ {
102
+ $activeTab = $this->getActiveTab();
103
+ $activeTab->setup();
104
+ }
105
+
106
+ /**
107
+ * Get the tabbed navigation for GDPR options page
108
+ *
109
+ * @return array
110
+ */
111
+ public function getNavigationData()
112
+ {
113
+ if (!count($this->tabs)) {
114
+ return [];
115
+ }
116
+
117
+ $navigation = [];
118
+
119
+ foreach ($this->tabs as $tab) {
120
+ /* @var $tab AdminTabInterface */
121
+ $navigation[$tab->getSlug()] = [
122
+ 'slug' => $tab->getSlug(),
123
+ 'url' => $this->getTabUrl($tab->getSlug()),
124
+ 'title' => $tab->getTitle(),
125
+ 'active' => false,
126
+ ];
127
+
128
+ if ($this->isTabActive($tab->getSlug())) {
129
+ $navigation[$tab->getSlug()]['active'] = true;
130
+ }
131
+ }
132
+
133
+ return $navigation;
134
+ }
135
+
136
+
137
+ /**
138
+ * todo: move to helper?
139
+ *
140
+ * @param $slug
141
+ * @return string
142
+ */
143
+ public function getTabUrl($slug)
144
+ {
145
+ return admin_url('tools.php?page=privacy&gdpr-tab=' . $slug);
146
+ }
147
+ }
src/Components/Consent/AdminTabConsent.php ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\Consent;
4
+
5
+ use Codelight\GDPR\Admin\AdminTab;
6
+
7
+ /**
8
+ * Handle rendering and saving the Consent tab on GDPR Options page
9
+ *
10
+ * Class AdminTabConsent
11
+ * @package Codelight\GDPR\Components\Consent
12
+ */
13
+ class AdminTabConsent extends AdminTab
14
+ {
15
+ /* @var string */
16
+ protected $slug = 'consent';
17
+
18
+ /* @var ConsentManager */
19
+ protected $consentManager;
20
+
21
+ /**
22
+ * AdminTabConsent constructor.
23
+ *
24
+ * @param ConsentManager $consentManager
25
+ */
26
+ public function __construct(ConsentManager $consentManager)
27
+ {
28
+ $this->consentManager = $consentManager;
29
+
30
+ $this->title = __('Consent', 'gdpr-admin');
31
+
32
+ // If we don't register the settings, WP will not allow this page to be submitted
33
+ $this->registerSetting('consent_types');
34
+ $this->registerSetting('consent_info');
35
+
36
+ $this->renderErrors();
37
+
38
+ // Register handler for this action
39
+ add_action('gdpr/admin/action/update_consent_data', [$this, 'updateConsentData']);
40
+ }
41
+
42
+ /**
43
+ * Initialize tab contents and register hooks
44
+ */
45
+ public function init()
46
+ {
47
+ $this->registerSettingSection(
48
+ 'gdpr_section_consent',
49
+ __('Consent', 'gdpr-admin'),
50
+ [$this, 'renderConsentForm']
51
+ );
52
+ }
53
+
54
+ /**
55
+ * Render the contents of the registered section
56
+ */
57
+ public function renderConsentForm()
58
+ {
59
+ $consentInfo = gdpr('options')->get('consent_info');
60
+
61
+ if (is_null($consentInfo)) {
62
+ $consentInfo = $this->getDefaultConsentInfo();
63
+ } elseif (!$consentInfo) {
64
+ $consentInfo = '';
65
+ }
66
+
67
+ $nonce = wp_create_nonce("gdpr/admin/action/update_consent_data");
68
+ $defaultConsentTypes = $this->consentManager->getDefaultConsentTypes();
69
+ $customConsentTypes = $this->consentManager->getCustomConsentTypes();
70
+
71
+ // todo: move to a filter
72
+ if (defined('ICL_LANGUAGE_CODE')) {
73
+ $prefix = ICL_LANGUAGE_CODE . '_';
74
+ } else {
75
+ $prefix = '';
76
+ }
77
+
78
+ echo gdpr('view')->render('admin/consent', compact('nonce', 'customConsentTypes', 'defaultConsentTypes', 'consentInfo', 'prefix'));
79
+ }
80
+
81
+ /**
82
+ * Save the submitted consent types
83
+ */
84
+ public function updateConsentData()
85
+ {
86
+ // Update additional information
87
+ if (isset($_POST['gdpr_consent_info'])) {
88
+ gdpr('options')->set('consent_info', wp_unslash($_POST['gdpr_consent_info']));
89
+ }
90
+
91
+ // Update consent types
92
+ if (isset($_POST['gdpr_consent_types']) && is_array($_POST['gdpr_consent_types'])) {
93
+ $consentTypes = $_POST['gdpr_consent_types'];
94
+ } else {
95
+ $consentTypes = [];
96
+ }
97
+
98
+ // Strip slashes which WP adds automatically
99
+ if (count($consentTypes)) {
100
+ foreach ($consentTypes as &$type) {
101
+ foreach ($type as $key => $item) {
102
+ if (is_array($item)) {
103
+ $type[$key] = array_map('wp_unslash', $item);
104
+ } else {
105
+ $type[$key] = wp_unslash($item);
106
+ }
107
+
108
+ if ('visible' === $key) {
109
+ $type[$key] = 1;
110
+ }
111
+ }
112
+ }
113
+ }
114
+
115
+ $errors = [];
116
+
117
+ if (!empty($consentTypes)) {
118
+ $errors = $this->validate($consentTypes);
119
+ }
120
+
121
+ if (!count($errors)) {
122
+ $this->consentManager->saveCustomConsentTypes($consentTypes);
123
+ } else {
124
+ $errorQuery = http_build_query($errors);
125
+ wp_safe_redirect(gdpr('helpers')->getAdminUrl('&gdpr-tab=consent&') . $errorQuery);
126
+ exit;
127
+ }
128
+ }
129
+
130
+ protected function validate($consentTypes)
131
+ {
132
+ $errors = [];
133
+
134
+ foreach ($consentTypes as $consentType) {
135
+ if (empty($consentType['slug'])) {
136
+ $errors['errors[]'] = 'slug-empty';
137
+ }
138
+
139
+ if (!preg_match('/^[A-Za-z0-9_-]+$/', $consentType['slug'])) {
140
+ $errors['errors[]'] = 'slug-invalid';
141
+ }
142
+
143
+ if (empty($consentType['title'])) {
144
+ $errors['errors[]'] = 'title-empty';
145
+ }
146
+ }
147
+
148
+ return $errors;
149
+ }
150
+
151
+ public function renderErrors()
152
+ {
153
+ if (isset($_GET['errors']) && count($_GET['errors'])) {
154
+
155
+ foreach ($_GET['errors'] as $error) {
156
+ if ('slug-empty' === $error) {
157
+ $message = __("Consent slug is a required field!", 'gdpr-admin');
158
+ gdpr('admin-error')->add('admin/notices/error', compact('message'));
159
+ }
160
+
161
+ if ('slug-invalid' === $error) {
162
+ $message = __("You may only use alphanumeric characters, dash and underscore in the consent slug field.", 'gdpr-admin');
163
+ gdpr('admin-error')->add('admin/notices/error', compact('message'));
164
+ }
165
+
166
+ if ('title-empty' === $error) {
167
+ $message = __("Consent title is a required field!", 'gdpr-admin');
168
+ gdpr('admin-error')->add('admin/notices/error', compact('message'));
169
+ }
170
+ }
171
+ }
172
+ }
173
+
174
+ /**
175
+ * @return string
176
+ */
177
+ public function getDefaultConsentInfo()
178
+ {
179
+ return 'To use this website, you accepted our Privacy Policy. If you wish to withdraw your acceptance, please use the "Delete my data" button below.';
180
+ }
181
+ }
src/Components/Consent/ConsentAdmin.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\Consent;
4
+
5
+ class ConsentAdmin
6
+ {
7
+ public function __construct()
8
+ {
9
+ gdpr()->bind(AdminTabConsent::class);
10
+
11
+ add_filter('gdpr/admin/tabs', [$this, 'registerAdminTab'], 20);
12
+ }
13
+
14
+ public function registerAdminTab($tabs)
15
+ {
16
+ $tabs['consent'] = gdpr(AdminTabConsent::class);
17
+
18
+ return $tabs;
19
+ }
20
+ }
src/Components/Consent/ConsentManager.php ADDED
@@ -0,0 +1,317 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\Consent;
4
+
5
+ /**
6
+ * Handles getting, saving and removing consent based on a whitelist
7
+ *
8
+ * Class ConsentManager
9
+ *
10
+ * @package Codelight\GDPR\Components\Consent
11
+ */
12
+ class ConsentManager
13
+ {
14
+ /* @var UserConsentModel */
15
+ protected $model;
16
+
17
+ /* @var array */
18
+ protected $defaultConsentTypes = [];
19
+
20
+ /* @var array */
21
+ protected $customConsentTypes = [];
22
+
23
+ /**
24
+ * ConsentManager constructor.
25
+ *
26
+ * @param UserConsentModel $model
27
+ */
28
+ public function __construct(UserConsentModel $model)
29
+ {
30
+ $this->model = $model;
31
+
32
+ add_action('init', [$this, 'registerCustomConsentTypes'], 0);
33
+ add_action('init', [$this, 'registerDefaultConsentTypes'], 0);
34
+
35
+ add_action('gdpr/data-subject/delete', [$this, 'delete']);
36
+ add_action('gdpr/data-subject/anonymize', [$this, 'anonymize'], 10, 2);
37
+ }
38
+
39
+ public function registerDefaultConsentTypes()
40
+ {
41
+ $policyPageUrl = get_permalink(gdpr('options')->get('policy_page'));
42
+
43
+ gdpr('consent')->register(
44
+ 'privacy-policy',
45
+ sprintf(
46
+ __('I accept the %sPrivacy Policy%s', 'gdpr'),
47
+ "<a href='{$policyPageUrl}' target='_blank'>",
48
+ "</a>"
49
+ ),
50
+ __('This consent is not visible by default. If someone wishes to withdraw it, they should simply request to delete all their data.', 'gdpr-admin'),
51
+ false
52
+ );
53
+
54
+ $termsPage = gdpr('options')->get('terms_page');
55
+ if ($termsPage) {
56
+ $termsPageUrl = get_permalink($termsPage);
57
+ } else {
58
+ $termsPageUrl = false;
59
+ }
60
+
61
+ if ($termsPageUrl) {
62
+ gdpr('consent')->register(
63
+ 'terms-conditions',
64
+ sprintf(
65
+ __('I accept the %sTerms & Conditions%s', 'gdpr'),
66
+ "<a href='{$termsPageUrl}' target='_blank'>",
67
+ "</a>"
68
+ ),
69
+ __('This consent is not visible by default. If someone wishes to withdraw it, they should simply request to delete all their data.', 'gdpr-admin'),
70
+ false
71
+ );
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Get a list of all registered consent types
77
+ *
78
+ * @return array
79
+ */
80
+ public function getConsentTypes()
81
+ {
82
+ return apply_filters('gdpr/consent/types', $this->getDefaultConsentTypes() + $this->getCustomConsentTypes());
83
+ }
84
+
85
+ /**
86
+ * Get all consent types registered by external sources, i.e. not stored in the database
87
+ *
88
+ * @return array
89
+ */
90
+ public function getDefaultConsentTypes()
91
+ {
92
+ return apply_filters('gdpr/consent/types/default', $this->defaultConsentTypes);
93
+ }
94
+
95
+ /**
96
+ * Get all consent types registered by the admin, i.e. stored in the database
97
+ *
98
+ * @return array
99
+ */
100
+ public function getCustomConsentTypes()
101
+ {
102
+ return apply_filters('gdpr/consent/types/custom', $this->customConsentTypes);
103
+ }
104
+
105
+ /**
106
+ * Register a *default* consent in the list of valid consents
107
+ *
108
+ * @param $consent
109
+ */
110
+ public function register($slug, $title, $description, $visible = true)
111
+ {
112
+ $this->defaultConsentTypes[$slug] = [
113
+ 'slug' => $slug,
114
+ 'title' => $title,
115
+ 'description' => $description,
116
+ 'visible' => $visible,
117
+ ];
118
+ }
119
+
120
+ /**
121
+ * Register consent types saved via WP admin
122
+ */
123
+ public function registerCustomConsentTypes()
124
+ {
125
+ $savedConsentTypes = gdpr('options')->get('consent_types');
126
+
127
+ if (is_array($savedConsentTypes) && count($savedConsentTypes)) {
128
+ foreach ($savedConsentTypes as $consentType) {
129
+ $this->customConsentTypes[$consentType['slug']] = [
130
+ 'slug' => isset($consentType['slug']) ? $consentType['slug'] : '',
131
+ 'title' => isset($consentType['title']) ? $consentType['title'] : '',
132
+ 'description' => isset($consentType['description']) ? $consentType['description'] : '',
133
+ 'visible' => isset($consentType['visible']) ? $consentType['visible'] : '',
134
+ ];
135
+ }
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Save the given consent types to database
141
+ *
142
+ * @param $consentTypes
143
+ */
144
+ public function saveCustomConsentTypes($consentTypes)
145
+ {
146
+ // Todo: validate to make sure something broken is not saved to DB
147
+ gdpr('options')->set('consent_types', $consentTypes);
148
+ }
149
+
150
+ /**
151
+ * Check if a consent is valid so that we don't write random stuff in the database by accident
152
+ *
153
+ * @param $consent
154
+ * @return bool
155
+ */
156
+ public function isRegisteredConsent($consent)
157
+ {
158
+ return isset($this->getConsentTypes()[$consent]);
159
+ }
160
+
161
+ /**
162
+ * Check if the given consent is valid. If not, throw error.
163
+ *
164
+ * @param $consent
165
+ */
166
+ protected function validateConsent($consent)
167
+ {
168
+ if (!$this->isRegisteredConsent($consent)) {
169
+ wp_die("Not a valid consent: " . esc_html($consent));
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Set a consent as 'given' for the data subject
175
+ *
176
+ * @param $email
177
+ * @param $consent
178
+ */
179
+ public function giveConsent($email, $consent)
180
+ {
181
+ $this->validateConsent($consent);
182
+
183
+ $validation = apply_filters('gdpr/consent/give', true, $email, $consent);
184
+
185
+ // If the data subject has already given this consent, do nothing
186
+ if ($this->model->given($email, $consent) || !$validation) {
187
+ return;
188
+ }
189
+
190
+ $this->model->give($email, $consent);
191
+ do_action('gdpr/consent/given', $email, $consent);
192
+ }
193
+
194
+ /**
195
+ * Set a consent as withdrawn for the data subject
196
+ *
197
+ * @param $email
198
+ * @param $consent
199
+ */
200
+ public function withdrawConsent($email, $consent)
201
+ {
202
+ $this->validateConsent($consent);
203
+
204
+ $validation = apply_filters('gdpr/consent/withdraw', true, $email, $consent);
205
+
206
+ // If the consent has never been given or if data subject has already withdrawn this consent, do nothing
207
+ if (!$this->model->exists($email, $consent) || $this->model->withdrawn($email, $consent) || !$validation) {
208
+ return;
209
+ }
210
+
211
+ $this->model->withdraw($email, $consent);
212
+ do_action('gdpr/consent/withdrawn', $email, $consent, 'withdrawn');
213
+ }
214
+
215
+ /**
216
+ * Remove consent given by subject
217
+ *
218
+ * @param $email
219
+ * @param $consent
220
+ */
221
+ public function deleteConsent($email, $consent)
222
+ {
223
+ $this->validateConsent($consent);
224
+
225
+ if ($this->model->given($email, $consent)) {
226
+ do_action('gdpr/consent/withdrawn', $email, $consent, 'deleted');
227
+ }
228
+
229
+ $this->model->delete($email, $consent);
230
+ }
231
+
232
+ /**
233
+ * Withdraw and anonymize a consent
234
+ *
235
+ * @param $email
236
+ * @param $consent
237
+ * @param $anonymizedId
238
+ */
239
+ public function anonymizeConsent($email, $consent, $anonymizedId)
240
+ {
241
+ $this->validateConsent($consent);
242
+
243
+ if ($this->model->given($email, $consent)) {
244
+ do_action('gdpr/consent/withdrawn', $email, $consent, 'anonymized');
245
+ }
246
+
247
+ $this->model->anonymize($email, $consent, $anonymizedId);
248
+ }
249
+
250
+ /**
251
+ * Get all consent given by subject
252
+ *
253
+ * @param $email
254
+ */
255
+ public function getAllConsents($email)
256
+ {
257
+ return $this->model->getAll($email);
258
+ }
259
+
260
+ /**
261
+ * Get the registered consent types and add 'given' field depending
262
+ * on whether or not the user has given this particular consent
263
+ *
264
+ * @param $dataSubjectConsents
265
+ * @return array
266
+ */
267
+ public function getConsentData($dataSubjectConsents)
268
+ {
269
+ $consentTypes = $this->getConsentTypes();
270
+ $consents = [];
271
+
272
+ foreach ($consentTypes as $slug => $consentType) {
273
+ if (in_array($slug, $dataSubjectConsents)) {
274
+ $consents[$slug] = $consentType;
275
+ }
276
+ }
277
+
278
+ return $consents;
279
+ }
280
+
281
+ /**
282
+ * Return a list of all data subjects who have given a particular consent
283
+ *
284
+ * @param $consent
285
+ */
286
+ public function getAllDataSubjectsByConsent($consent)
287
+ {
288
+ // Todo
289
+ }
290
+
291
+ /**
292
+ * Withdraw and delete all consents given by a data subject
293
+ *
294
+ * @param $email
295
+ */
296
+ public function delete($email)
297
+ {
298
+ $consents = $this->getAllConsents($email);
299
+ foreach ($consents as $consent) {
300
+ $this->deleteConsent($email, $consent);
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Withdraw and anonymize all consents given by a data subject
306
+ *
307
+ * @param $email
308
+ * @param $anonymizedId
309
+ */
310
+ public function anonymize($email, $anonymizedId)
311
+ {
312
+ $consents = $this->getAllConsents($email);
313
+ foreach ($consents as $consent) {
314
+ $this->anonymizeConsent($email, $consent, $anonymizedId);
315
+ }
316
+ }
317
+ }
src/Components/Consent/UserConsentModel.php ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\Consent;
4
+
5
+ /**
6
+ * Class UserConsentModel
7
+ *
8
+ * @package Codelight\GDPR\Components\Consent
9
+ */
10
+ class UserConsentModel
11
+ {
12
+ /* @var string */
13
+ public $tableName;
14
+
15
+ /* @var string */
16
+ public $version = '1.0';
17
+
18
+ /* @var string */
19
+ public $primaryKey = 'id';
20
+
21
+ /**
22
+ * UserConsentModel constructor.
23
+ */
24
+ public function __construct()
25
+ {
26
+ $this->setTableName();
27
+
28
+ // todo: cleanup
29
+ // global $wpdb;
30
+ //$wpdb->query('TRUNCATE TABLE wp_gdpr_consent');
31
+ }
32
+
33
+ /**
34
+ * Set the table name with wpdb-s prefix
35
+ */
36
+ protected function setTableName()
37
+ {
38
+ global $wpdb;
39
+ $this->tableName = $wpdb->prefix . 'gdpr_consent';
40
+ }
41
+
42
+ /**
43
+ * Check if a user has given a consent
44
+ *
45
+ * @param $email
46
+ * @param $consent
47
+ * @return int
48
+ */
49
+ public function given($email, $consent)
50
+ {
51
+ global $wpdb;
52
+
53
+ return count($wpdb->get_results($wpdb->prepare(
54
+ "SELECT * FROM {$this->tableName} WHERE email = %s AND consent = %s AND status = 1;",
55
+ $email,
56
+ $consent
57
+ )));
58
+ }
59
+
60
+ /**
61
+ * Check if a user has withdrawn a consent
62
+ *
63
+ * @param $email
64
+ * @param $consent
65
+ * @return int
66
+ */
67
+ public function withdrawn($email, $consent)
68
+ {
69
+ global $wpdb;
70
+
71
+ return count($wpdb->get_results($wpdb->prepare(
72
+ "SELECT * FROM {$this->tableName} WHERE email = %s AND consent = %s AND status = 0;",
73
+ $email,
74
+ $consent
75
+ )));
76
+ }
77
+
78
+ /**
79
+ * Check if a consent exists in the database
80
+ *
81
+ * @param $email
82
+ * @param $consent
83
+ * @return array|null|object
84
+ */
85
+ public function exists($email, $consent)
86
+ {
87
+ global $wpdb;
88
+
89
+ return count($wpdb->get_results($wpdb->prepare(
90
+ "SELECT * FROM {$this->tableName} WHERE email = %s AND consent = %s;",
91
+ $email,
92
+ $consent
93
+ )));
94
+ }
95
+
96
+ /**
97
+ * Set a consent to 'given'
98
+ *
99
+ * @param $email
100
+ * @param $consent
101
+ * @param $status
102
+ * @return false|int
103
+ */
104
+ public function give($email, $consent)
105
+ {
106
+ $this->set($email, $consent, 1);
107
+ }
108
+
109
+ /**
110
+ * Set a consent to 'withdrawn'
111
+ *
112
+ * @param $email
113
+ * @param $consent
114
+ * @param $status
115
+ * @return false|int
116
+ */
117
+ public function withdraw($email, $consent)
118
+ {
119
+ $this->set($email, $consent, 0);
120
+ }
121
+
122
+ /**
123
+ * Set a consent to 'given' or 'withdrawn'
124
+ *
125
+ * @param $email
126
+ * @param $consent
127
+ * @param $status
128
+ * @return false|int
129
+ */
130
+ protected function set($email, $consent, $status, $version = 1)
131
+ {
132
+ global $wpdb;
133
+
134
+ if ($this->exists($email, $consent)) {
135
+ return $wpdb->update(
136
+ $this->tableName,
137
+ [
138
+ 'version' => $version,
139
+ 'consent' => $consent,
140
+ 'status' => $status,
141
+ 'updated_at' => date("Y-m-d H:i:s"),
142
+ 'ip' => $_SERVER['REMOTE_ADDR'],
143
+ ],
144
+ [
145
+ 'email' => $email,
146
+ 'consent' => $consent,
147
+ ]
148
+ );
149
+ } else {
150
+ return $wpdb->insert(
151
+ $this->tableName,
152
+ [
153
+ 'email' => $email,
154
+ 'version' => $version,
155
+ 'consent' => $consent,
156
+ 'status' => $status,
157
+ 'updated_at' => date("Y-m-d H:i:s"),
158
+ 'ip' => $_SERVER['REMOTE_ADDR'],
159
+ ]
160
+ );
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Get all consent given by data subject
166
+ *
167
+ * @param $email
168
+ */
169
+ public function getAll($email)
170
+ {
171
+ global $wpdb;
172
+
173
+ return array_column($wpdb->get_results($wpdb->prepare(
174
+ "SELECT * FROM {$this->tableName} WHERE email = %s and status = 1;",
175
+ $email
176
+ )), 'consent');
177
+ }
178
+
179
+ /**
180
+ * Remove a consent row from the database
181
+ *
182
+ * @param $email
183
+ * @param $consent
184
+ * @return false|int
185
+ */
186
+ public function delete($email, $consent)
187
+ {
188
+ global $wpdb;
189
+
190
+ return $wpdb->delete(
191
+ $this->tableName,
192
+ [
193
+ 'email' => $email,
194
+ 'consent' => $consent,
195
+ ]
196
+ );
197
+ }
198
+
199
+ /**
200
+ * Withdraw consent and anonymize data subject's email
201
+ *
202
+ * @param $email
203
+ * @param $consent
204
+ * @param $anonymizedId
205
+ * @return false|int
206
+ */
207
+ public function anonymize($email, $consent, $anonymizedId)
208
+ {
209
+ global $wpdb;
210
+
211
+ if ($this->exists($email, $consent)) {
212
+ return $wpdb->update(
213
+ $this->tableName,
214
+ [
215
+ 'email' => $anonymizedId,
216
+ 'consent' => $consent,
217
+ 'status' => 0,
218
+ 'updated_at' => date("Y-m-d H:i:s"),
219
+ 'ip' => $_SERVER['REMOTE_ADDR'],
220
+ ],
221
+ [
222
+ 'email' => $email,
223
+ 'consent' => $consent,
224
+ ]
225
+ );
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Get columns and formats
231
+ */
232
+ public function getColumns()
233
+ {
234
+ return [
235
+ 'id' => '%d',
236
+ 'version' => '%d',
237
+ 'email' => '%s',
238
+ 'consent' => '%s',
239
+ 'status' => '%d',
240
+ 'updated_at' => '%s',
241
+ 'ip' => '%s'
242
+ //'valid_until' => '%s',
243
+ ];
244
+ }
245
+
246
+ /**
247
+ * Get default column values
248
+ */
249
+ public function getColumnDefaults()
250
+ {
251
+ return [
252
+ 'id' => '',
253
+ 'version' => 1,
254
+ 'email' => '',
255
+ 'consent' => '',
256
+ 'status' => '',
257
+ 'updated_at' => '',
258
+ 'ip' => '',
259
+ //'valid_until' => '',
260
+ ];
261
+ }
262
+
263
+ /**
264
+ * Create the table
265
+ */
266
+ public function createTable()
267
+ {
268
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
269
+ $sql = "CREATE TABLE " . $this->tableName . " (
270
+ id bigint(20) NOT NULL AUTO_INCREMENT,
271
+ version int NOT NULL,
272
+ email varchar(64) NOT NULL,
273
+ consent varchar(128) NOT NULL,
274
+ status tinyint NOT NULL,
275
+ updated_at TIMESTAMP NULL,
276
+ ip varchar(64) NOT NULL,
277
+ PRIMARY KEY (id)
278
+ ) CHARACTER SET utf8 COLLATE utf8_general_ci;";
279
+ dbDelta($sql);
280
+ update_option($this->tableName . '_db_version', $this->version);
281
+ }
282
+ }
src/Components/PrivacyPolicy/AdminTabPrivacyPolicy.php ADDED
@@ -0,0 +1,471 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\PrivacyPolicy;
4
+
5
+ use Codelight\GDPR\Admin\AdminTab;
6
+
7
+ class AdminTabPrivacyPolicy extends AdminTab
8
+ {
9
+ /* @var string */
10
+ protected $slug = 'privacy-policy';
11
+
12
+ /* @var PolicyGenerator */
13
+ protected $policyGenerator;
14
+
15
+ public function __construct(PolicyGenerator $policyGenerator)
16
+ {
17
+ $this->policyGenerator = $policyGenerator;
18
+
19
+ $this->title = __('Privacy Policy', 'gdpr-admin');
20
+
21
+ $this->registerSetting('gdpr_company_name');
22
+ $this->registerSetting('gdpr_contact_email');
23
+ $this->registerSetting('gdpr_company_location');
24
+
25
+ $this->registerSetting('gdpr_representative_contact_name');
26
+ $this->registerSetting('gdpr_representative_contact_email');
27
+ $this->registerSetting('gdpr_representative_contact_phone');
28
+
29
+ $this->registerSetting('gdpr_dpa_website');
30
+ $this->registerSetting('gdpr_dpa_email');
31
+ $this->registerSetting('gdpr_dpa_phone');
32
+
33
+ $this->registerSetting('gdpr_has_dpo');
34
+ $this->registerSetting('gdpr_dpo_name');
35
+ $this->registerSetting('gdpr_dpo_email');
36
+
37
+ /*
38
+ $this->registerSetting('gdpr_pp_data_gathered_1');
39
+ $this->registerSetting('gdpr_pp_data_gathered_2');
40
+ $this->registerSetting('gdpr_pp_data_gathered_3');
41
+ $this->registerSetting('gdpr_pp_data_gathered_4');
42
+
43
+ $this->registerSetting('gdpr_pp_data_usage_1');
44
+ $this->registerSetting('gdpr_pp_data_usage_2');
45
+ $this->registerSetting('gdpr_pp_data_usage_3');
46
+ $this->registerSetting('gdpr_pp_data_usage_4');
47
+
48
+ $this->registerSetting('gdpr_pp_data_partners');
49
+ */
50
+
51
+ add_action('gdpr/admin/action/privacy-policy/generate', [$this, 'generatePolicy']);
52
+ }
53
+
54
+ public function init()
55
+ {
56
+ /**
57
+ * General settings
58
+ */
59
+ $this->registerSettingSection(
60
+ 'gdpr_section_privacy_policy',
61
+ __('Privacy Policy', 'gdpr-admin'),
62
+ [$this, 'renderHeader']
63
+ );
64
+
65
+ /**
66
+ * Company info
67
+ */
68
+ $this->registerSettingSection(
69
+ 'gdpr_section_privacy_policy_company',
70
+ __('Company information', 'gdpr-admin')
71
+ );
72
+
73
+ $this->registerSettingField(
74
+ 'gdpr_company_name',
75
+ __('Company Name', 'gdpr-admin'),
76
+ [$this, 'renderCompanyNameHtml'],
77
+ 'gdpr_section_privacy_policy_company'
78
+ );
79
+
80
+ $this->registerSettingField(
81
+ 'gdpr_company_email',
82
+ __('Company Email', 'gdpr-admin'),
83
+ [$this, 'renderCompanyEmailHtml'],
84
+ 'gdpr_section_privacy_policy_company'
85
+ );
86
+
87
+ $this->registerSettingField(
88
+ 'gdpr_company_location',
89
+ __('Company Location', 'gdpr-admin'),
90
+ [$this, 'renderCompanyLocationHtml'],
91
+ 'gdpr_section_privacy_policy_company'
92
+ );
93
+
94
+ /**
95
+ * Representative
96
+ */
97
+ $this->registerSettingSection(
98
+ 'gdpr_section_privacy_policy_representative',
99
+ false,
100
+ [$this, 'renderRepresentativeOpen']
101
+ );
102
+
103
+ $this->registerSettingField(
104
+ 'gdpr_representative_contact_name',
105
+ __('Representative Contact Name', 'gdpr-admin'),
106
+ [$this, 'renderRepresentativeContactName'],
107
+ 'gdpr_section_privacy_policy_representative'
108
+ );
109
+
110
+ $this->registerSettingField(
111
+ 'gdpr_representative_contact_email',
112
+ __('Representative Contact Email', 'gdpr-admin'),
113
+ [$this, 'renderRepresentativeContactEmail'],
114
+ 'gdpr_section_privacy_policy_representative'
115
+ );
116
+
117
+ $this->registerSettingField(
118
+ 'gdpr_representative_contact_phone',
119
+ __('Representative Contact Phone', 'gdpr-admin'),
120
+ [$this, 'renderRepresentativeContactPhone'],
121
+ 'gdpr_section_privacy_policy_representative'
122
+ );
123
+
124
+ $this->registerSettingSection(
125
+ 'gdpr_section_privacy_policy_representative_close',
126
+ false,
127
+ [$this, 'renderRepresentativeClose']
128
+ );
129
+
130
+ /**
131
+ * DPA
132
+ */
133
+ $this->registerSettingSection(
134
+ 'gdpr_section_privacy_policy_dpa',
135
+ __('Data Protection Authority', 'gdpr-admin'),
136
+ [$this, 'renderDpaJS']
137
+ );
138
+
139
+ $this->registerSettingField(
140
+ 'gdpr_dpa_website',
141
+ __('Data Protection Authority Website', 'gdpr-admin'),
142
+ [$this, 'renderDpaWebsite'],
143
+ 'gdpr_section_privacy_policy_dpa'
144
+ );
145
+
146
+ $this->registerSettingField(
147
+ 'gdpr_dpa_email',
148
+ __('Data Protection Authority Email', 'gdpr-admin'),
149
+ [$this, 'renderDpaEmail'],
150
+ 'gdpr_section_privacy_policy_dpa'
151
+ );
152
+
153
+ $this->registerSettingField(
154
+ 'gdpr_dpa_phone',
155
+ __('Data Protection Authority Phone', 'gdpr-admin'),
156
+ [$this, 'renderDpaPhone'],
157
+ 'gdpr_section_privacy_policy_dpa'
158
+ );
159
+
160
+ /**
161
+ * DPO
162
+ */
163
+ $this->registerSettingSection(
164
+ 'gdpr_section_privacy_policy_dpo',
165
+ __('Data Protection Officer', 'gdpr-admin'),
166
+ function() {
167
+ echo "<a href='https://codelight.eu/wordpress-gdpr-framework/knowledge-base/do-i-need-to-appoint-data-protection-officer-dpo/' target='_blank'>";
168
+ echo __('Knowledge base: Do I need to appoint a Data Protection Officer?');
169
+ echo "</a>";
170
+ }
171
+ );
172
+
173
+ $this->registerSettingField(
174
+ 'gdpr_has_dpo',
175
+ __('Data Protection Officer', 'gdpr-admin'),
176
+ [$this, 'renderHasDPOHtml'],
177
+ 'gdpr_section_privacy_policy_dpo'
178
+ );
179
+
180
+ $this->registerSettingField(
181
+ 'gdpr_dpo_name',
182
+ __('Data Protection Officer Name', 'gdpr-admin'),
183
+ [$this, 'renderDPONameHtml'],
184
+ 'gdpr_section_privacy_policy_dpo',
185
+ ['class' => 'gdpr-dpo hidden']
186
+ );
187
+
188
+ $this->registerSettingField(
189
+ 'gdpr_dpo_email',
190
+ __('Data Protection Officer Email', 'gdpr-admin'),
191
+ [$this, 'renderDPOEmailHtml'],
192
+ 'gdpr_section_privacy_policy_dpo',
193
+ ['class' => 'gdpr-dpo hidden']
194
+ );
195
+
196
+ /**
197
+ * Policy contents
198
+ */
199
+
200
+ /*
201
+ $this->registerSettingSection(
202
+ 'gdpr_section_privacy_policy_contents',
203
+ __('Policy Contents', 'gdpr-admin')
204
+ );
205
+
206
+ $this->registerSettingField(
207
+ 'gdpr_pp_data_gathered_1',
208
+ __('Information you have provided us with', 'gdpr-admin'),
209
+ [$this, 'renderDataGathered1'],
210
+ 'gdpr_section_privacy_policy_contents',
211
+ ['class' => 'gdpr-dpo hidden']
212
+ );
213
+ */
214
+ }
215
+
216
+ /*
217
+ public function renderDataGathered1()
218
+ {
219
+ $contents = gdpr('options')->get('pp_data_gathered_1') ? gdpr('options')->get('pp_data_gathered_1') : 'default';
220
+
221
+ echo wp_editor(
222
+ $contents,
223
+ 'pp_data_gathered_1',
224
+ [
225
+ 'media_buttons' => false,
226
+ 'editor_height' => 200,
227
+ ]
228
+ );
229
+ }
230
+ */
231
+
232
+ public function renderHeader()
233
+ {
234
+ echo gdpr('view')->render('admin/privacy-policy/header');
235
+ }
236
+
237
+ /**
238
+ * Company info
239
+ */
240
+
241
+ public function renderCompanyNameHtml()
242
+ {
243
+ $value = gdpr('options')->get('company_name') ? esc_attr(gdpr('options')->get('company_name')) : '';
244
+ $placeholder = __('Company Name', 'gdpr-admin');
245
+ echo "<input name='gdpr_company_name' placeholder='{$placeholder}' value='{$value}'>";
246
+ }
247
+
248
+ public function renderCompanyEmailHtml()
249
+ {
250
+ $value = gdpr('options')->get('contact_email') ? esc_attr(gdpr('options')->get('contact_email')) : '';
251
+ $placeholder = __('Contact Email', 'gdpr-admin');
252
+ echo "<input type='email' name='gdpr_contact_email' placeholder='{$placeholder}' value='{$value}'>";
253
+ }
254
+
255
+ public function renderCompanyLocationHtml()
256
+ {
257
+ $country = gdpr('options')->get('company_location') ? gdpr('options')->get('company_location') : '';
258
+ $countrySelectOptions = gdpr('helpers')->getCountrySelectOptions($country);
259
+ echo gdpr('view')->render('admin/privacy-policy/company-location', compact('countrySelectOptions'));
260
+ }
261
+
262
+ /**
263
+ * Representative contact
264
+ */
265
+
266
+ public function renderRepresentativeOpen()
267
+ {
268
+ echo "<span class='gdpr-representative hidden'>";
269
+ echo "<h3>";
270
+ echo __('Representative Contact', 'gdpr-admin');
271
+ echo "</h3>";
272
+ echo "<a href='https://codelight.eu/wordpress-gdpr-framework/knowledge-base/do-i-need-to-appoint-an-eu-based-representative/' target='_blank'>";
273
+ echo __('Knowledge base: Do I need to appoint an EU-based representative?');
274
+ echo "</a>";
275
+ }
276
+
277
+ public function renderRepresentativeContactName()
278
+ {
279
+ $value = gdpr('options')->get('representative_contact_name') ? esc_attr(gdpr('options')->get('representative_contact_name')) : '';
280
+ $placeholder = __('Representative Contact Name', 'gdpr-admin');
281
+ echo "<input name='gdpr_representative_contact_name' placeholder='{$placeholder}' value='{$value}'>";
282
+ }
283
+
284
+ public function renderRepresentativeContactEmail()
285
+ {
286
+ $value = gdpr('options')->get('representative_contact_email') ? esc_attr(gdpr('options')->get('representative_contact_email')) : '';
287
+ $placeholder = __('Representative Contact Email', 'gdpr-admin');
288
+ echo "<input type='email' name='gdpr_representative_contact_email' placeholder='{$placeholder}' value='{$value}'>";
289
+ }
290
+
291
+ public function renderRepresentativeContactPhone()
292
+ {
293
+ $value = gdpr('options')->get('representative_contact_phone') ? esc_attr(gdpr('options')->get('representative_contact_phone')) : '';
294
+ $placeholder = __('Representative Contact Phone', 'gdpr-admin');
295
+ echo "<input name='gdpr_representative_contact_phone' placeholder='{$placeholder}' value='{$value}'>";
296
+ }
297
+
298
+ public function renderRepresentativeClose()
299
+ {
300
+ echo "</span>";
301
+ }
302
+
303
+ /**
304
+ * Data Protection Authority
305
+ */
306
+ public function renderDpaJS()
307
+ {
308
+ echo "<a href='https://codelight.eu/wordpress-gdpr-framework/knowledge-base/do-i-need-to-appoint-an-eu-based-representative/' target='_blank'>";
309
+ echo 'See the <a href="http://ec.europa.eu/justice/data-protection/article-29/structure/data-protection-authorities/index_en.htm" target="_blank">list of contacts here</a>';
310
+ echo "</a>";
311
+
312
+
313
+
314
+ $dpaData = json_encode(gdpr('helpers')->getDataProtectionAuthorities());
315
+ echo gdpr('view')->render('admin/privacy-policy/dpa', compact('dpaData'));
316
+ }
317
+
318
+ public function renderDpaWebsite()
319
+ {
320
+ $value = gdpr('options')->get('dpa_website') ? esc_attr(gdpr('options')->get('dpa_website')) : '';
321
+ $placeholder = __('Data Protection Authority Website', 'gdpr-admin');
322
+ echo "<input name='gdpr_dpa_website' id='gdpr_dpa_website' placeholder='{$placeholder}' value='{$value}' data-set='{$value}'>";
323
+ }
324
+
325
+ public function renderDpaEmail()
326
+ {
327
+ $value = gdpr('options')->get('dpa_email') ? esc_attr(gdpr('options')->get('dpa_email')) : '';
328
+ $placeholder = __('Data Protection Authority Email', 'gdpr-admin');
329
+ echo "<input type='email' name='gdpr_dpa_email' id='gdpr_dpa_email' placeholder='{$placeholder}' value='{$value}' data-set='{$value}'>";
330
+ }
331
+
332
+ public function renderDpaPhone()
333
+ {
334
+ $value = gdpr('options')->get('dpa_phone') ? esc_attr(gdpr('options')->get('dpa_phone')) : '';
335
+ $placeholder = __('Data Protection Authority Phone', 'gdpr-admin');
336
+ echo "<input name='gdpr_dpa_phone' id='gdpr_dpa_phone' placeholder='{$placeholder}' value='{$value}' data-set='{$value}'>";
337
+ }
338
+
339
+ /**
340
+ * Data Protection Officer
341
+ */
342
+
343
+ public function renderHasDPOHtml()
344
+ {
345
+ $hasDPO = gdpr('options')->get('has_dpo');
346
+ echo gdpr('view')->render('admin/privacy-policy/has-dpo', compact('hasDPO'));
347
+ }
348
+
349
+ public function renderDPONameHtml()
350
+ {
351
+ $value = gdpr('options')->get('dpo_name') ? esc_attr(gdpr('options')->get('dpo_name')) : '';
352
+ $placeholder = __('DPO Name', 'gdpr-admin');
353
+ echo "<input name='gdpr_dpo_name' placeholder='{$placeholder}' value='{$value}'>";
354
+ }
355
+
356
+ public function renderDPOEmailHtml()
357
+ {
358
+ $value = gdpr('options')->get('dpo_email') ? esc_attr(gdpr('options')->get('dpo_email')) : '';
359
+ $placeholder = __('DPO Name', 'gdpr-admin');
360
+ echo "<input type='email' name='gdpr_dpo_email' placeholder='{$placeholder}' value='{$value}'>";
361
+ }
362
+
363
+
364
+ public function generatePolicy()
365
+ {
366
+ $policyPage = gdpr('options')->get('policy_page');
367
+
368
+ // todo: handle errors
369
+ if ( ! $policyPage) {
370
+ return;
371
+ }
372
+
373
+ $policy = gdpr('view')->render(
374
+ 'policy/policy'
375
+ );
376
+
377
+ wp_update_post([
378
+ 'ID' => $policyPage,
379
+ 'post_content' => $policy,
380
+ ]);
381
+
382
+ wp_safe_redirect(gdpr('helpers')->getAdminUrl('&gdpr-tab=privacy-policy&gdpr_notice=policy_generated'));
383
+ }
384
+
385
+ /**
386
+ * Render either the settings or the generated policy
387
+ */
388
+ public function renderContents()
389
+ {
390
+ if (isset($_GET['generate']) && 'yes' == $_GET['generate']) {
391
+ return $this->renderPolicy();
392
+ } else {
393
+ return $this->renderSettings();
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Render the contents including settings fields, sections and submit button.
399
+ * Trigger hooks for rendering content before and after the settings fields.
400
+ *
401
+ * @return string
402
+ */
403
+ public function renderSettings()
404
+ {
405
+ ob_start();
406
+
407
+ do_action("gdpr/tabs/{$this->getSlug()}/before", $this);
408
+ $this->settingsFields($this->getOptionsGroupName());
409
+ do_settings_sections($this->getOptionsGroupName());
410
+ do_action("gdpr/tabs/{$this->getSlug()}/after", $this);
411
+
412
+ $this->renderSubmitButton();
413
+
414
+ return ob_get_clean();
415
+ }
416
+
417
+ public function renderPolicy()
418
+ {
419
+ $policyPageId = gdpr('options')->get('policy_page');
420
+ if ($policyPageId) {
421
+ $policyUrl = get_edit_post_link($policyPageId);
422
+ } else {
423
+ $policyUrl = false;
424
+ }
425
+
426
+ $editor = $this->getPolicyEditor();
427
+ $backUrl = gdpr('helpers')->getAdminUrl('&gdpr-tab=privacy-policy');
428
+
429
+ return gdpr('view')->render('admin/privacy-policy/generated', compact('editor', 'policyUrl', 'backUrl'));
430
+ }
431
+
432
+ protected function getPolicyEditor()
433
+ {
434
+ ob_start();
435
+
436
+ wp_editor(
437
+ wp_kses_post($this->policyGenerator->generate()),
438
+ 'gdpr_policy',
439
+ [
440
+ 'media_buttons' => false,
441
+ 'editor_height' => 600,
442
+ 'teeny' => true,
443
+ 'editor_css' => '<style>#mceu_16 { display: none; }</style>'
444
+ ]
445
+ );
446
+
447
+ return ob_get_clean();
448
+ }
449
+
450
+ /**
451
+ * Render WP's default submit button
452
+ */
453
+ public function renderSubmitButton()
454
+ {
455
+ submit_button(__('Save & Generate Policy', 'gdpr-admin'));
456
+ }
457
+
458
+ /**
459
+ * In order to set up a proper redirect to the generated policy
460
+ * after saving settings, we modify the way wp_nonce_field is called and insert our own referer input.
461
+ *
462
+ * @param $optionGroup
463
+ */
464
+ public function settingsFields($optionGroup)
465
+ {
466
+ echo "<input type='hidden' name='option_page' value='" . esc_attr($optionGroup) . "' />";
467
+ echo '<input type="hidden" name="action" value="update" />';
468
+ wp_nonce_field("$optionGroup-options", '_wpnonce', false);
469
+ echo '<input type="hidden" name="_wp_http_referer" value="'. esc_attr( wp_unslash( $_SERVER['REQUEST_URI'] ) . '&generate=yes' ) . '" />';
470
+ }
471
+ }
src/Components/PrivacyPolicy/PolicyGenerator.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\PrivacyPolicy;
4
+
5
+ class PolicyGenerator
6
+ {
7
+ public function generate()
8
+ {
9
+ return gdpr('view')->render(
10
+ 'policy/policy',
11
+ $this->getData()
12
+ );
13
+ }
14
+
15
+ public function getData()
16
+ {
17
+ $location = gdpr('options')->get('company_location');
18
+ $date = date(get_option('date_format'));
19
+
20
+ return [
21
+ 'companyName' => gdpr('options')->get('company_name'),
22
+ 'companyLocation' => $location,
23
+ 'contactEmail' => gdpr('options')->get('contact_email') ?
24
+ gdpr('options')->get('contact_email') :
25
+ get_option('admin_email'),
26
+
27
+ 'hasRepresentative' => gdpr('helpers')->countryNeedsRepresentative($location),
28
+ 'representativeContactName' => gdpr('options')->get('representative_contact_name'),
29
+ 'representativeContactEmail' => gdpr('options')->get('representative_contact_email'),
30
+ 'representativeContactPhone' => gdpr('options')->get('representative_contact_phone'),
31
+
32
+ 'dpaWebsite' => gdpr('options')->get('dpa_name'),
33
+ 'dpaEmail' => gdpr('options')->get('dpa_email'),
34
+ 'dpaPhone' => gdpr('options')->get('dpa_phone'),
35
+
36
+ 'hasDpo' => gdpr('options')->get('has_dpo'),
37
+ 'dpoName' => gdpr('options')->get('dpo_name'),
38
+ 'dpoEmail' => gdpr('options')->get('dpo_email'),
39
+
40
+ 'hasTerms' => gdpr('options')->get('terms_page'),
41
+
42
+ 'date' => $date,
43
+ ];
44
+ }
45
+ }
src/Components/PrivacyPolicy/PrivacyPolicy.php ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\PrivacyPolicy;
4
+
5
+ /**
6
+ * Handles putting together and rendering the privacy policy page
7
+ *
8
+ * Class PrivacyPolicy
9
+ *
10
+ * @package Codelight\GDPR\Components\PrivacyPolicy
11
+ */
12
+ class PrivacyPolicy
13
+ {
14
+ public function __construct()
15
+ {
16
+ add_filter('gdpr/admin/tabs', [$this, 'registerAdminTab'], 35);
17
+
18
+ add_shortcode('gdpr_privacy', [$this, 'doShortcode']);
19
+ add_shortcode('gdpr_privacy_policy_url', [$this, 'renderUrlShortcode']);
20
+ add_shortcode('gdpr_privacy_policy_link', [$this, 'renderLinkShortcode']);
21
+ }
22
+
23
+ public function registerAdminTab($tabs)
24
+ {
25
+ $tabs['privacy-policy'] = gdpr()->make(AdminTabPrivacyPolicy::class);
26
+
27
+ return $tabs;
28
+ }
29
+
30
+ public function doShortcode($attributes)
31
+ {
32
+ $attributes = shortcode_atts([
33
+ 'item' => null,
34
+ ], $attributes);
35
+
36
+ switch ($attributes['item']) {
37
+ case 'company_name':
38
+ return esc_html(gdpr('options')->get('company_name'));
39
+ case 'company_email':
40
+ return esc_html(gdpr('options')->get('contact_email'));
41
+ case 'company_email_link':
42
+ $email = antispambot(gdpr('options')->get('contact_email'));
43
+ return "<a href='mailto:{$email}'>{$email}</a>";
44
+ case 'dpo_name':
45
+ return esc_html(gdpr('options')->get('dpo_name'));
46
+ case 'dpo_email':
47
+ return esc_html(gdpr('options')->get('dpo_email'));
48
+ case 'dpo_email_link':
49
+ $email = antispambot(gdpr('options')->get('dpo_email'));
50
+ return "<a href='mailto:{$email}'>{$email}</a>";
51
+ case 'rep_name':
52
+ return esc_html(gdpr('options')->get('representative_contact_name'));
53
+ case 'rep_email':
54
+ return esc_html(gdpr('options')->get('representative_contact_email'));
55
+ case 'rep_email_link':
56
+ $email = antispambot(gdpr('options')->get('representative_contact_email'));
57
+ return "<a href='mailto:{$email}'>{$email}</a>";
58
+ case 'authority_website':
59
+ return esc_html(gdpr('options')->get('dpa_website'));
60
+ case 'authority_email':
61
+ return esc_html(gdpr('options')->get('dpa_email'));
62
+ case 'authority_email_link':
63
+ $email = antispambot(gdpr('options')->get('dpa_email'));
64
+ return "<a href='mailto:{$email}'>{$email}</a>";
65
+ case 'authority_phone':
66
+ return esc_html(gdpr('options')->get('dpa_phone'));
67
+ case null:
68
+ return '';
69
+ }
70
+
71
+ return '';
72
+ }
73
+
74
+ public function renderUrlShortcode()
75
+ {
76
+ return gdpr('helpers')->getPrivacyPolicyPageUrl();
77
+ }
78
+
79
+ public function renderLinkShortcode($attributes)
80
+ {
81
+ $attributes = shortcode_atts([
82
+ 'title' => __('Privacy Policy', 'gdpr'),
83
+ ], $attributes);
84
+
85
+ $url = gdpr('helpers')->getPrivacyPolicyPageUrl();
86
+
87
+ return
88
+ "<a href='{$url}'>" .
89
+ esc_html($attributes['title']) .
90
+ "</a>";
91
+ }
92
+ }
src/Components/PrivacyToolsPage/PrivacyToolsPage.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\PrivacyToolsPage;
4
+
5
+ class PrivacyToolsPage
6
+ {
7
+ public function __construct()
8
+ {
9
+ gdpr()->singleton(PrivacyToolsPageController::class);
10
+ gdpr()->make(PrivacyToolsPageShortcode::class);
11
+
12
+ gdpr()->singleton(PrivacyToolsPageShortcode::class);
13
+ gdpr()->make(PrivacyToolsPageShortcode::class);
14
+ }
15
+ }
src/Components/PrivacyToolsPage/PrivacyToolsPageController.php ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\PrivacyToolsPage;
4
+
5
+ use Codelight\GDPR\DataSubject\DataSubject;
6
+ use Codelight\GDPR\DataSubject\DataSubjectAuthenticator;
7
+ use Codelight\GDPR\DataSubject\DataSubjectIdentificator;
8
+ use Codelight\GDPR\DataSubject\DataSubjectManager;
9
+ use Codelight\GDPR\DataSubject\DataExporter;
10
+
11
+ /**
12
+ * Handle the data page on front-end
13
+ *
14
+ * Class DataPageController
15
+ *
16
+ * @package Codelight\GDPR\Components\DataPage
17
+ */
18
+ class PrivacyToolsPageController
19
+ {
20
+ /* @var DataSubjectAuthenticator */
21
+ protected $dataSubjectAuthenticator;
22
+
23
+ /* @var DataSubjectIdentificator */
24
+ protected $dataSubjectIdentificator;
25
+
26
+ /* @var DataSubjectManager */
27
+ protected $dataSubjectManager;
28
+
29
+ /**
30
+ * DataPageController constructor.
31
+ *
32
+ * @param DataSubjectIdentificator $dataSubjectIdentificator
33
+ * @param DataSubjectManager $dataSubjectManager
34
+ */
35
+ public function __construct(
36
+ DataSubjectAuthenticator $dataSubjectAuthenticator,
37
+ DataSubjectIdentificator $dataSubjectIdentificator,
38
+ DataSubjectManager $dataSubjectManager,
39
+ DataExporter $dataExporter)
40
+ {
41
+ $this->dataSubjectAuthenticator = $dataSubjectAuthenticator;
42
+ $this->dataSubjectIdentificator = $dataSubjectIdentificator;
43
+ $this->dataSubjectManager = $dataSubjectManager;
44
+ $this->dataExporter = $dataExporter;
45
+
46
+ if (!gdpr('options')->get('enable')) {
47
+ return;
48
+ }
49
+
50
+ $this->setup();
51
+ }
52
+
53
+ protected function setup()
54
+ {
55
+ // Listen to 'identify' action and send an email
56
+ add_action('gdpr/frontend/action/identify', [$this, 'sendIdentificationEmail']);
57
+
58
+ add_action('gdpr/frontend/privacy-tools-page/content', [$this, 'renderConsentForm'], 10, 2);
59
+ add_action('gdpr/frontend/privacy-tools-page/content', [$this, 'renderExportForm'], 20, 2);
60
+ add_action('gdpr/frontend/privacy-tools-page/content', [$this, 'renderDeleteForm'], 30, 2);
61
+
62
+ add_action('gdpr/frontend/privacy-tools-page/action/withdraw_consent', [$this, 'withdrawConsent'], 10, 2);
63
+ add_action('gdpr/frontend/privacy-tools-page/action/export', [$this, 'export'], 10, 2);
64
+ add_action('gdpr/frontend/privacy-tools-page/action/forget', [$this, 'forget'], 10, 2);
65
+ }
66
+
67
+ /**
68
+ * If the given email address exists as a data subject, send an authentication email to that address
69
+ */
70
+ public function sendIdentificationEmail()
71
+ {
72
+ // Additional safety check
73
+ if (!is_email($_REQUEST['email'])) {
74
+ $this->redirect(['gdpr_notice' => 'invalid_email']);
75
+ }
76
+
77
+ if ($this->dataSubjectIdentificator->isDataSubject($_REQUEST['email'])) {
78
+ $this->dataSubjectIdentificator->sendIdentificationEmail($_REQUEST['email']);
79
+ } else {
80
+ $this->dataSubjectIdentificator->sendNoDataFoundEmail($_REQUEST['email']);
81
+ }
82
+
83
+ $this->redirect(['gdpr_notice' => 'email_sent']);
84
+ }
85
+
86
+ /**
87
+ * Render the page contents.
88
+ * This is only called via the shortcode.
89
+ */
90
+ public function render()
91
+ {
92
+ $dataSubject = $this->dataSubjectAuthenticator->authenticate();
93
+ $this->renderNotices();
94
+
95
+ if ($dataSubject) {
96
+ $this->renderPrivacyTools($dataSubject);
97
+ } else {
98
+ $this->renderIdentificationForm();
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Display notices to the user.
104
+ * The contents of the notices are currently hardcoded inside the template.
105
+ */
106
+ protected function renderNotices()
107
+ {
108
+ if (!isset($_REQUEST['gdpr_notice'])) {
109
+ return;
110
+ }
111
+
112
+ echo gdpr('view')->render('privacy-tools/notices');
113
+ }
114
+
115
+ /**
116
+ * Render the contents of the identification form
117
+ */
118
+ protected function renderIdentificationForm()
119
+ {
120
+ $nonce = wp_create_nonce('gdpr/frontend/action/identify');
121
+ echo gdpr('view')->render('privacy-tools/form-identify', compact('nonce', 'notices'));
122
+ }
123
+
124
+ /**
125
+ * Render the contents of the Privacy Tools page
126
+ *
127
+ * @param DataSubject $dataSubject
128
+ */
129
+ protected function renderPrivacyTools(DataSubject $dataSubject)
130
+ {
131
+ $email = $dataSubject->getEmail();
132
+ echo gdpr('view')->render('privacy-tools/privacy-tools', compact('dataSubject', 'email'));
133
+ }
134
+
135
+ /**
136
+ * Render the form that allows withdrawing consent
137
+ *
138
+ * @param DataSubject $dataSubject
139
+ */
140
+ public function renderConsentForm(DataSubject $dataSubject)
141
+ {
142
+ $consentData = $dataSubject->getVisibleConsentData();
143
+
144
+ foreach ($consentData as &$item) {
145
+ $item['withdraw_url'] = add_query_arg([
146
+ 'gdpr_action' => 'withdraw_consent',
147
+ 'gdpr_nonce' => wp_create_nonce("gdpr/frontend/privacy-tools-page/action/withdraw_consent"),
148
+ 'email' => $dataSubject->getEmail(),
149
+ 'consent' => $item['slug'],
150
+ ]);
151
+ }
152
+
153
+ $consentInfo = apply_filters('the_content', gdpr('options')->get('consent_info'));
154
+
155
+ echo gdpr('view')->render(
156
+ "privacy-tools/form-consent",
157
+ compact('consentData', 'consentInfo')
158
+ );
159
+ }
160
+
161
+ /**
162
+ * Render the form that allows the data subject to export their data
163
+ *
164
+ * @param DataSubject $dataSubject
165
+ */
166
+ public function renderExportForm(DataSubject $dataSubject)
167
+ {
168
+ $email = $dataSubject->getEmail();
169
+ $nonce = wp_create_nonce("gdpr/frontend/privacy-tools-page/action/export");
170
+
171
+ echo gdpr('view')->render(
172
+ "privacy-tools/form-export",
173
+ compact('email', 'nonce')
174
+ );
175
+ }
176
+
177
+ /**
178
+ * Render the form that allows the data subject to delete their data
179
+ *
180
+ * @param DataSubject $dataSubject
181
+ */
182
+ public function renderDeleteForm(DataSubject $dataSubject)
183
+ {
184
+ // Let's not allow admins to delete themselves
185
+ if (current_user_can('manage_options')) {
186
+ return;
187
+ }
188
+
189
+ $action = 'forget';
190
+ $email = $dataSubject->getEmail();
191
+ $nonce = wp_create_nonce("gdpr/frontend/privacy-tools-page/action/forget");
192
+
193
+ echo gdpr('view')->render(
194
+ "privacy-tools/form-delete",
195
+ compact('action', 'email', 'nonce')
196
+ );
197
+ }
198
+
199
+ /**
200
+ * Withdraw the consent
201
+ *
202
+ * @param DataSubject $dataSubject
203
+ */
204
+ public function withdrawConsent(DataSubject $dataSubject)
205
+ {
206
+ $dataSubject->withdrawConsent($_REQUEST['consent']);
207
+ $this->redirect(['gdpr_notice' => 'consent_withdrawn']);
208
+ }
209
+
210
+ /**
211
+ * Trigger the export action.
212
+ *
213
+ * @param DataSubject $dataSubject
214
+ */
215
+ public function export(DataSubject $dataSubject)
216
+ {
217
+ $data = $dataSubject->export($_REQUEST['gdpr_format']);
218
+
219
+ if (!is_null($data)) {
220
+ // If there is data, download it
221
+ $this->dataExporter->export($data, $dataSubject, $_REQUEST['gdpr_format']);
222
+ } else {
223
+ // If there's no data, then show notification that your request has been sent.
224
+ $this->redirect(['gdpr_notice' => 'request_sent']);
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Trigger the forget action.
230
+ *
231
+ * @param DataSubject $dataSubject
232
+ */
233
+ public function forget(DataSubject $dataSubject)
234
+ {
235
+ $deleted = $dataSubject->forget();
236
+
237
+ if ($deleted) {
238
+ $this->dataSubjectAuthenticator->deleteSession();
239
+ } else {
240
+ // If request was sent to admin, then show notification
241
+ $this->redirect(['gdpr_notice' => 'request_sent']);
242
+ }
243
+
244
+ }
245
+
246
+ /**
247
+ * Redirect the visitor to an appropriate location
248
+ *
249
+ * @param array $args
250
+ * @param null $baseUrl
251
+ */
252
+ protected function redirect($args = [], $baseUrl = null)
253
+ {
254
+ if (!$baseUrl) {
255
+ $baseUrl = get_permalink(gdpr('options')->get('tools_page'));
256
+ }
257
+
258
+ wp_safe_redirect(add_query_arg($args, $baseUrl));
259
+ exit;
260
+ }
261
+ }
src/Components/PrivacyToolsPage/PrivacyToolsPageShortcode.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\PrivacyToolsPage;
4
+
5
+ class PrivacyToolsPageShortcode
6
+ {
7
+ public function __construct(PrivacyToolsPageController $controller)
8
+ {
9
+ $this->controller = $controller;
10
+
11
+ add_shortcode('gdpr_privacy_tools', [$this, 'renderPage']);
12
+ add_shortcode('gdpr_privacy_tools_url', [$this, 'renderUrlShortcode']);
13
+ add_shortcode('gdpr_privacy_tools_link', [$this, 'renderLinkShortcode']);
14
+ }
15
+
16
+ public function renderPage()
17
+ {
18
+ if (!gdpr('options')->get('enable')) {
19
+ return __('This page is currently disabled.', 'gdpr');
20
+ }
21
+
22
+ ob_start();
23
+ $this->controller->render();
24
+ return ob_get_clean();
25
+ }
26
+
27
+ public function renderUrlShortcode()
28
+ {
29
+ return gdpr('helpers')->getPrivacyToolsPageUrl();
30
+ }
31
+
32
+ public function renderLinkShortcode($attributes)
33
+ {
34
+ $attributes = shortcode_atts([
35
+ 'title' => __('Privacy Tools', 'gdpr'),
36
+ ], $attributes);
37
+
38
+ $url = gdpr('helpers')->getPrivacyToolsPageUrl();
39
+
40
+ return
41
+ "<a href='{$url}'>" .
42
+ esc_html($attributes['title']) .
43
+ "</a>";
44
+ }
45
+ }
src/Components/Support/AdminTabSupport.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\Support;
4
+
5
+ use Codelight\GDPR\Admin\AdminTab;
6
+
7
+ class AdminTabSupport extends AdminTab
8
+ {
9
+ protected $slug = 'support';
10
+
11
+ public function __construct()
12
+ {
13
+ $this->title = __('Support', 'gdpr-admin');
14
+ }
15
+
16
+ public function init()
17
+ {
18
+ $this->registerSettingSection(
19
+ 'gdpr-section-support',
20
+ __('Support', 'gdpr-admin'),
21
+ [$this, 'renderTab']
22
+ );
23
+ }
24
+
25
+ public function renderTab()
26
+ {
27
+ echo gdpr('view')->render('admin/support/contents');
28
+ }
29
+
30
+ public function renderSubmitButton()
31
+ {
32
+ // Intentionally left blank
33
+ }
34
+ }
src/Components/Support/Support.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\Support;
4
+
5
+ class Support
6
+ {
7
+ public function __construct()
8
+ {
9
+ add_filter('gdpr/admin/tabs', [$this, 'registerTab'], 40);
10
+ }
11
+
12
+ public function registerTab($tabs)
13
+ {
14
+ $tabs['support'] = gdpr()->make(AdminTabSupport::class);
15
+
16
+ return $tabs;
17
+ }
18
+ }
src/Components/Themes/Themes.php ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\Themes;
4
+
5
+ class Themes
6
+ {
7
+ protected $theme;
8
+
9
+ public $supportedThemes = [
10
+ 'twentyseventeen',
11
+ 'twentysixteen',
12
+ 'storefront'
13
+ ];
14
+
15
+ public function __construct()
16
+ {
17
+ $this->theme = get_option('stylesheet');
18
+
19
+ if (!$this->isCurrentThemeSupported() || !gdpr('options')->get('enable_theme_compatibility')) {
20
+ return;
21
+ }
22
+
23
+ // If both pages aren't defined, bail
24
+ $privacyPolicy = gdpr('options')->get('policy_page');
25
+ $privacyToolsPage = gdpr('options')->get('tools_page');
26
+
27
+ if (!$privacyPolicy || !$privacyToolsPage) {
28
+ return;
29
+ }
30
+
31
+ $theme = $this->theme;
32
+ $this->$theme();
33
+ }
34
+
35
+ public function isCurrentThemeSupported()
36
+ {
37
+ return in_array($this->theme, $this->supportedThemes);
38
+ }
39
+
40
+ public function getCurrentThemeName()
41
+ {
42
+ return $this->theme;
43
+ }
44
+
45
+ public function twentyseventeen()
46
+ {
47
+ add_action("get_template_part_template-parts/footer/site", [$this, 'renderTwentyseventeenFooterLinks'], 10, 2);
48
+ }
49
+
50
+ public function twentysixteen()
51
+ {
52
+ add_action("twentysixteen_credits", [$this, 'renderTwentysixteenFooterLinks']);
53
+ }
54
+
55
+ public function storefront()
56
+ {
57
+ // I feel slightly dirty, but also clever
58
+ add_filter("storefront_credit_link", [$this, 'renderStorefrontFooterLinks']);
59
+ }
60
+
61
+ public function renderTwentyseventeenFooterLinks($slug, $name)
62
+ {
63
+ if ('info' !== $name) {
64
+ return;
65
+ }
66
+
67
+ $privacyPolicyUrl = get_permalink(gdpr('options')->get('policy_page'));
68
+ $privacyToolsPageUrl = get_permalink(gdpr('options')->get('tools_page'));
69
+
70
+ echo gdpr('view')->render(
71
+ 'themes/twentyseventeen/footer',
72
+ compact('privacyPolicyUrl', 'privacyToolsPageUrl')
73
+ );
74
+ }
75
+
76
+ public function renderTwentysixteenFooterLinks()
77
+ {
78
+ $privacyPolicyUrl = get_permalink(gdpr('options')->get('policy_page'));
79
+ $privacyToolsPageUrl = get_permalink(gdpr('options')->get('tools_page'));
80
+
81
+ echo gdpr('view')->render(
82
+ 'themes/twentysixteen/footer',
83
+ compact('privacyPolicyUrl', 'privacyToolsPageUrl')
84
+ );
85
+ }
86
+
87
+ public function renderStorefrontFooterLinks($value)
88
+ {
89
+ $privacyPolicyUrl = get_permalink(gdpr('options')->get('policy_page'));
90
+ $privacyToolsPageUrl = get_permalink(gdpr('options')->get('tools_page'));
91
+
92
+ echo gdpr('view')->render(
93
+ 'themes/storefront/footer',
94
+ compact('privacyPolicyUrl', 'privacyToolsPageUrl')
95
+ );
96
+
97
+ return $value;
98
+ }
99
+ }
src/Components/WordpressComments/WordpressComments.php ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\WordpressComments;
4
+
5
+ use Codelight\GDPR\DataSubject\DataSubject;
6
+ use Codelight\GDPR\DataSubject\DataSubjectManager;
7
+
8
+ class WordpressComments
9
+ {
10
+ /* @var DataSubjectManager */
11
+ protected $dataSubjectManager;
12
+
13
+ public function __construct(DataSubjectManager $dataSubjectManager)
14
+ {
15
+ $this->dataSubjectManager = $dataSubjectManager;
16
+
17
+ $this->setup();
18
+ }
19
+
20
+ public function setup()
21
+ {
22
+ if (gdpr('options')->get('policy_page')) {
23
+ add_action('comment_form_after_fields', [$this, 'maybeAddCommentFormCheckbox']);
24
+ add_action('comment_form_logged_in_after', [$this, 'maybeAddCommentFormCheckbox']);
25
+
26
+ add_filter('preprocess_comment', [$this, 'validate']);
27
+ }
28
+
29
+ add_filter('gdpr/data-subject/data', [$this, 'getExportData'], 1, 2);
30
+ add_action('gdpr/data-subject/delete', [$this, 'deleteComments']);
31
+ add_action('gdpr/data-subject/anonymize', [$this, 'deleteComments']);
32
+ }
33
+
34
+ /**
35
+ * Check if consent is needed
36
+ *
37
+ * @return bool
38
+ */
39
+ public function needsConsent($email = null)
40
+ {
41
+ if ($email) {
42
+ $dataSubject = $this->dataSubjectManager->getByEmail($email);
43
+ } else {
44
+ $dataSubject = $this->dataSubjectManager->getByLoggedInUser();
45
+ }
46
+ return !($dataSubject && $dataSubject->hasConsented('privacy-policy'));
47
+ }
48
+
49
+ /**
50
+ * If consent is needed, render the checkbox
51
+ *
52
+ * @param $fields
53
+ */
54
+ public function maybeAddCommentFormCheckbox()
55
+ {
56
+ $email = isset($_POST['email']) ? $_POST['email'] : null;
57
+
58
+ if (!$this->needsConsent($email)) {
59
+ return;
60
+ }
61
+
62
+ $privacyPolicyUrl = get_permalink(gdpr('options')->get('policy_page'));
63
+ $termsPage = gdpr('options')->get('terms_page');
64
+ if ($termsPage) {
65
+ $termsUrl = get_permalink($termsPage);
66
+ } else {
67
+ $termsUrl = false;
68
+ }
69
+
70
+ echo gdpr('view')->render(
71
+ 'modules/wordpress-comments/terms-checkbox',
72
+ compact('termsUrl', 'privacyPolicyUrl')
73
+ );
74
+ }
75
+
76
+ /**
77
+ * If consent is needed, validate it
78
+ */
79
+ public function validate($commentData)
80
+ {
81
+ $email = isset($_POST['email']) ? $_POST['email'] : null;
82
+
83
+ if (!$this->needsConsent($email)) {
84
+ return $commentData;
85
+ }
86
+
87
+ if (!isset($_POST['gdpr_terms']) || !$_POST['gdpr_terms']) {
88
+ wp_die(
89
+ sprintf(
90
+ __('%sERROR:%s You need to accept the terms and conditions to post a comment.'),
91
+ '<strong>',
92
+ '</strong>'
93
+ )
94
+ );
95
+ } else {
96
+ if (is_user_logged_in()) {
97
+ $dataSubject = $this->dataSubjectManager->getByLoggedInUser();
98
+ } else {
99
+ $dataSubject = $this->dataSubjectManager->getByEmail($email);
100
+ }
101
+ $dataSubject->giveConsent('privacy-policy');
102
+ }
103
+
104
+ return $commentData;
105
+ }
106
+
107
+ /**
108
+ * Add comments as well as comment meta to export data
109
+ *
110
+ * @param $data
111
+ * @param $email
112
+ * @param $dataSubject
113
+ * @return mixed
114
+ */
115
+ public function getExportData($data, $email)
116
+ {
117
+ $comments = $this->getCommentsByEmail($email);
118
+
119
+ if (count($comments)) {
120
+
121
+ foreach ($comments as $comment) {
122
+ /* @var $comment \WP_Comment */
123
+
124
+ $commentData = [
125
+ 'comment_author' => $comment->comment_author,
126
+ 'comment_author_email' => $comment->comment_author_email,
127
+ 'comment_author_url' => $comment->comment_author_url,
128
+ 'comment_author_IP' => $comment->comment_author_IP,
129
+ 'comment_date' => $comment->comment_date,
130
+ 'comment_date_gmt' => $comment->comment_date_gmt,
131
+ 'comment_content' => $comment->comment_content,
132
+ 'comment_approved' => $comment->comment_approved,
133
+ 'comment_agent' => $comment->comment_agent,
134
+ ];
135
+
136
+ $commentMeta = get_comment_meta($comment->comment_ID);
137
+ if (!empty($commentMeta)) {
138
+ $commentData['comment_meta'] = $commentMeta;
139
+ }
140
+
141
+ $data['comments'][] = $commentData;
142
+ }
143
+ }
144
+
145
+ return $data;
146
+ }
147
+
148
+ public function deleteComments($email)
149
+ {
150
+ $comments = $this->getCommentsByEmail($email);
151
+
152
+ if (!count($comments)) {
153
+ return;
154
+ }
155
+
156
+ foreach ($comments as $comment) {
157
+ /* @var $comment \WP_Comment */
158
+ wp_delete_comment($comment->comment_ID, true);
159
+ }
160
+ }
161
+
162
+ public function getCommentsByEmail($email)
163
+ {
164
+ if (!$email || !is_email($email)) {
165
+ return [];
166
+ }
167
+
168
+ $query = new \WP_Comment_Query;
169
+ return $query->query([
170
+ 'author_email' => $email,
171
+ ]);
172
+ }
173
+ }
src/Components/WordpressUser/Controllers/DashboardDataPageController.php ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\WordpressUser\Controllers;
4
+
5
+ use Codelight\GDPR\DataSubject\DataExporter;
6
+ use Codelight\GDPR\DataSubject\DataSubject;
7
+ use Codelight\GDPR\DataSubject\DataSubjectAuthenticator;
8
+
9
+ /**
10
+ * Handles Users > Privacy Tools page
11
+ *
12
+ * Class DashboardDataPageController
13
+ *
14
+ * @package Codelight\GDPR\Modules\WordpressUser\Controllers
15
+ */
16
+ class DashboardDataPageController
17
+ {
18
+ /**
19
+ * DashboardDataPageController constructor.
20
+ *
21
+ * @param DataExporter $dataExporter
22
+ */
23
+ public function __construct(DataExporter $dataExporter, DataSubjectAuthenticator $dataSubjectAuthenticator)
24
+ {
25
+ $this->dataExporter = $dataExporter;
26
+ $this->dataSubjectAuthenticator = $dataSubjectAuthenticator;
27
+
28
+ add_action('gdpr/dashboard/privacy-tools/content', [$this, 'renderHeader'], 10);
29
+ add_action('gdpr/dashboard/privacy-tools/content', [$this, 'renderConsentForm'], 20);
30
+ add_action('gdpr/dashboard/privacy-tools/content', [$this, 'renderExportForm'], 30);
31
+ add_action('gdpr/dashboard/privacy-tools/content', [$this, 'renderDeleteForm'], 40);
32
+
33
+ add_action('gdpr/dashboard/privacy-tools/action/withdraw_consent', [$this, 'withdrawConsent']);
34
+ add_action('gdpr/dashboard/privacy-tools/action/export', [$this, 'export']);
35
+ add_action('gdpr/dashboard/privacy-tools/action/forget', [$this, 'forget']);
36
+
37
+ add_action('admin_notices', [$this, 'renderAdminNotices']);
38
+ }
39
+
40
+ /**
41
+ * Render success notices via admin_notice action
42
+ */
43
+ public function renderAdminNotices()
44
+ {
45
+ if ('profile_page_gdpr-profile' !== get_current_screen()->base) {
46
+ return;
47
+ }
48
+
49
+ if (!isset($_REQUEST['gdpr_notice'])) {
50
+ return;
51
+ }
52
+
53
+ if ('request_sent' === $_REQUEST['gdpr_notice']) {
54
+ $message = __('We have received your request and will reply within 30 days.', 'gdpr');
55
+ $class = 'notice notice-success';
56
+ }
57
+
58
+ if ('consent_withdrawn' === $_REQUEST['gdpr_notice']) {
59
+ $message = __('Consent withdrawn.', 'gdpr');
60
+ $class = 'notice notice-success';
61
+ }
62
+
63
+ echo gdpr('view')->render('admin/notice', compact('message', 'class'));
64
+ }
65
+
66
+ /**
67
+ * Render page header
68
+ */
69
+ public function renderHeader()
70
+ {
71
+ echo gdpr('view')->render(
72
+ "modules/wordpress-user/dashboard/data-page/header"
73
+ );
74
+ }
75
+
76
+ /**
77
+ * Render the consent form
78
+ *
79
+ * @param DataSubject $dataSubject
80
+ */
81
+ public function renderConsentForm(DataSubject $dataSubject)
82
+ {
83
+ $consentData = $dataSubject->getVisibleConsentData();
84
+
85
+ foreach ($consentData as &$item) {
86
+ $item['withdraw_url'] = add_query_arg([
87
+ 'gdpr_action' => 'withdraw_consent',
88
+ 'gdpr_nonce' => wp_create_nonce("gdpr/dashboard/privacy-tools/action/withdraw_consent"),
89
+ 'email' => $dataSubject->getEmail(),
90
+ 'consent' => $item['slug'],
91
+ ]);
92
+ }
93
+
94
+ $consentInfo = apply_filters('the_content', gdpr('options')->get('consent_info'));
95
+
96
+ echo gdpr('view')->render(
97
+ "modules/wordpress-user/dashboard/data-page/form-consent",
98
+ compact('consentData', 'consentInfo')
99
+ );
100
+ }
101
+
102
+ /**
103
+ * Render the buttons that allow exporting data
104
+ */
105
+ public function renderExportForm()
106
+ {
107
+ $exportHTMLUrl = add_query_arg([
108
+ 'gdpr_action' => 'export',
109
+ 'gdpr_format' => 'html',
110
+ 'gdpr_nonce' => wp_create_nonce("gdpr/dashboard/privacy-tools/action/export"),
111
+ ]);
112
+
113
+ $exportJSONUrl = add_query_arg([
114
+ 'gdpr_action' => 'export',
115
+ 'gdpr_format' => 'json',
116
+ 'gdpr_nonce' => wp_create_nonce("gdpr/dashboard/privacy-tools/action/export"),
117
+ ]);
118
+
119
+ echo gdpr('view')->render(
120
+ "modules/wordpress-user/dashboard/form-export",
121
+ compact('exportHTMLUrl', 'exportJSONUrl')
122
+ );
123
+ }
124
+
125
+ /**
126
+ * Render the delete data button
127
+ */
128
+ public function renderDeleteForm()
129
+ {
130
+ $showDelete = !current_user_can('manage_options');
131
+ $url = add_query_arg([
132
+ 'gdpr_action' => 'forget',
133
+ 'gdpr_nonce' => wp_create_nonce("gdpr/dashboard/privacy-tools/action/forget"),
134
+ ]);
135
+
136
+ echo gdpr('view')->render(
137
+ "modules/wordpress-user/dashboard/data-page/form-delete",
138
+ compact('url', 'showDelete')
139
+ );
140
+ }
141
+
142
+ /**
143
+ * @param DataSubject $dataSubject
144
+ */
145
+ public function withdrawConsent(DataSubject $dataSubject)
146
+ {
147
+ $dataSubject->withdrawConsent($_REQUEST['consent']);
148
+ $this->redirect(['gdpr_notice' => 'consent_withdrawn']);
149
+ }
150
+
151
+ /**
152
+ * @param DataSubject $dataSubject
153
+ */
154
+ public function export(DataSubject $dataSubject)
155
+ {
156
+ $data = $dataSubject->export($_REQUEST['gdpr_format']);
157
+
158
+ if (!is_null($data)) {
159
+ // If there is data, download it
160
+ $this->dataExporter->export($data, $dataSubject, $_REQUEST['gdpr_format']);
161
+ } else {
162
+ // If there's no data, then show notification that your request has been sent.
163
+ $this->redirect(['gdpr_notice' => 'request_sent']);
164
+ }
165
+ }
166
+
167
+ /**
168
+ * @param DataSubject $dataSubject
169
+ */
170
+ public function forget(DataSubject $dataSubject)
171
+ {
172
+ $status = $dataSubject->forget();
173
+
174
+ if (!$status) {
175
+ $this->redirect(['gdpr_notice' => 'request_sent']);
176
+ } else {
177
+ $this->dataSubjectAuthenticator->deleteSession();
178
+ $this->redirect([], '/');
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Redirect the visitor to an appropriate location
184
+ *
185
+ * @param array $args
186
+ * @param null $baseUrl
187
+ */
188
+ protected function redirect($args = [], $baseUrl = null)
189
+ {
190
+ if (!$baseUrl) {
191
+ $baseUrl = gdpr('helpers')->getDashboardDataPageUrl();
192
+ }
193
+
194
+ wp_safe_redirect(add_query_arg($args, $baseUrl));
195
+ exit;
196
+ }
197
+ }
src/Components/WordpressUser/Controllers/DashboardProfilePageController.php ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\WordpressUser\Controllers;
4
+
5
+ use Codelight\GDPR\DataSubject\DataExporter;
6
+ use Codelight\GDPR\DataSubject\DataSubject;
7
+ use Codelight\GDPR\DataSubject\DataSubjectManager;
8
+
9
+ class DashboardProfilePageController
10
+ {
11
+ public function __construct(DataSubjectManager $dataSubjectManager, DataExporter $dataExporter)
12
+ {
13
+ $this->dataSubjectManager = $dataSubjectManager;
14
+ $this->dataExporter = $dataExporter;
15
+
16
+ add_action('gdpr/dashboard/profile-page/content', [$this, 'renderHeader'], 10);
17
+ add_action('gdpr/dashboard/profile-page/content', [$this, 'renderConsentTable'], 20);
18
+ add_action('gdpr/dashboard/profile-page/content', [$this, 'renderExportForm'], 30);
19
+ add_action('gdpr/dashboard/profile-page/content', [$this, 'renderDeleteForm'], 40);
20
+
21
+ add_action('gdpr/admin/action/export', [$this, 'export']);
22
+ add_action('gdpr/admin/action/forget', [$this, 'forget']);
23
+ }
24
+
25
+ protected function isUserAnonymized(DataSubject $dataSubject)
26
+ {
27
+ return !$dataSubject->getEmail();
28
+ }
29
+
30
+ public function renderHeader(DataSubject $dataSubject)
31
+ {
32
+ $isAnonymized = $this->isUserAnonymized($dataSubject);
33
+
34
+ echo gdpr('view')->render(
35
+ "modules/wordpress-user/dashboard/profile-page/header",
36
+ compact('isAnonymized')
37
+ );
38
+ }
39
+
40
+ public function renderConsentTable(DataSubject $dataSubject)
41
+ {
42
+ if ($this->isUserAnonymized($dataSubject)) {
43
+ return;
44
+ }
45
+
46
+ $consentData = $dataSubject->getConsentData();
47
+
48
+ echo gdpr('view')->render(
49
+ "modules/wordpress-user/dashboard/profile-page/table-consent",
50
+ compact('consentData')
51
+ );
52
+ }
53
+
54
+ public function renderExportForm(DataSubject $dataSubject)
55
+ {
56
+ if ($this->isUserAnonymized($dataSubject)) {
57
+ return;
58
+ }
59
+
60
+ $exportHTMLUrl = add_query_arg([
61
+ 'gdpr_action' => 'export',
62
+ 'gdpr_format' => 'html',
63
+ 'gdpr_email' => $dataSubject->getEmail(),
64
+ 'gdpr_nonce' => wp_create_nonce("gdpr/admin/action/export"),
65
+ ]);
66
+
67
+ $exportJSONUrl = add_query_arg([
68
+ 'gdpr_action' => 'export',
69
+ 'gdpr_format' => 'json',
70
+ 'gdpr_email' => $dataSubject->getEmail(),
71
+ 'gdpr_nonce' => wp_create_nonce("gdpr/admin/action/export"),
72
+ ]);
73
+
74
+ echo gdpr('view')->render(
75
+ "modules/wordpress-user/dashboard/form-export",
76
+ compact('exportHTMLUrl', 'exportJSONUrl')
77
+ );
78
+ }
79
+
80
+ public function renderDeleteForm(DataSubject $dataSubject)
81
+ {
82
+ if ($this->isUserAnonymized($dataSubject)) {
83
+ return;
84
+ }
85
+
86
+ // Hide the delete button away from site admins on their own profile page to avoid accidents
87
+ $showDelete = !(current_user_can('manage_options') && wp_get_current_user()->ID === $dataSubject->getUserId());
88
+
89
+ $anonymizeUrl = add_query_arg([
90
+ 'gdpr_email' => $dataSubject->getEmail(),
91
+ 'gdpr_action' => 'forget',
92
+ 'gdpr_force_action' => 'anonymize',
93
+ 'gdpr_nonce' => wp_create_nonce("gdpr/admin/action/forget"),
94
+ ]);
95
+
96
+ $deleteUrl = add_query_arg([
97
+ 'gdpr_email' => $dataSubject->getEmail(),
98
+ 'gdpr_action' => 'forget',
99
+ 'gdpr_force_action' => 'delete',
100
+ 'gdpr_nonce' => wp_create_nonce("gdpr/admin/action/forget"),
101
+ ]);
102
+
103
+ echo gdpr('view')->render(
104
+ "modules/wordpress-user/dashboard/profile-page/form-delete",
105
+ compact('anonymizeUrl', 'deleteUrl', 'showDelete')
106
+ );
107
+ }
108
+
109
+ public function export()
110
+ {
111
+ $dataSubject = $this->dataSubjectManager->getByEmail($_REQUEST['gdpr_email']);
112
+ $data = $dataSubject->export($_REQUEST['gdpr_format'], true);
113
+ $this->dataExporter->export($data, $dataSubject, $_REQUEST['gdpr_format']);
114
+ }
115
+
116
+ public function forget()
117
+ {
118
+ $dataSubject = $this->dataSubjectManager->getByEmail($_REQUEST['gdpr_email']);
119
+ $dataSubject->forget($_REQUEST['gdpr_force_action']);
120
+
121
+ wp_safe_redirect(admin_url('users.php'));
122
+ }
123
+ }
src/Components/WordpressUser/DataManager.php ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\WordpressUser;
4
+
5
+ use Codelight\GDPR\DataSubject\DataSubject;
6
+
7
+ class DataManager
8
+ {
9
+ public function getData(DataSubject $dataSubject)
10
+ {
11
+ $user = $dataSubject->getUser();
12
+ if ($user) {
13
+ $meta = get_user_meta($user->ID);
14
+ $data['meta'] = $meta;
15
+ } else {
16
+ $data = [];
17
+ }
18
+
19
+ $whitelist = [
20
+ 'nickname',
21
+ 'first_name',
22
+ 'last_name',
23
+ 'description',
24
+ 'locale',
25
+ 'community-events-location',
26
+ ];
27
+
28
+ if (isset($data['meta']) && count($data['meta'])) {
29
+ foreach ($data['meta'] as $key => $value) {
30
+ if (!in_array($key, $whitelist)) {
31
+ unset($data['meta'][$key]);
32
+ }
33
+ }
34
+ }
35
+
36
+ // Remove session keys. Just in case.
37
+ if (isset($meta) && isset($meta['session_tokens']) && count($meta['session_tokens'])) {
38
+ foreach ($meta['session_tokens'] as $token) {
39
+ foreach (unserialize($token) as $key => $tokenData) {
40
+ $data['meta']['session_tokens'][] = $tokenData;
41
+ }
42
+ }
43
+ }
44
+
45
+ /*
46
+ $blacklist = [
47
+ 'use_ssl',
48
+ 'show_admin_bar_front',
49
+ 'wp_capabilities',
50
+ 'wp_user_level',
51
+ 'dismissed_wp_pointers',
52
+ 'show_welcome_panel',
53
+ 'wp_dashboard_quick_press_last_post_id',
54
+ 'wp_user-settings',
55
+ 'wp_user-settings-time',
56
+ 'closedpostboxes_page',
57
+ 'metaboxhidden_page',
58
+ 'session_tokens',
59
+ 'managenav-menuscolumnshidden',
60
+ 'metaboxhidden_nav-menus',
61
+ 'nav_menu_recently_edited',
62
+ 'acf_user_settings',
63
+ ];
64
+
65
+ // Blacklist some data
66
+ if (isset($data['meta']) && count($data['meta'])) {
67
+ foreach ($data['meta'] as $key => $value) {
68
+ if (in_array($key, $blacklist)) {
69
+ unset($data['meta'][$key]);
70
+ }
71
+ }
72
+
73
+ $data['meta'] = array_diff_assoc($data['meta'], $blacklist);
74
+ }
75
+ */
76
+
77
+ return apply_filters('gdpr/wordpress-user/export/data', $data);
78
+ }
79
+
80
+ public function deleteUser(DataSubject $dataSubject, $reassign = null)
81
+ {
82
+ require_once(ABSPATH . 'wp-admin/includes/user.php');
83
+
84
+ $reassignOption = gdpr('options')->get('delete_action_reassign');
85
+ if ('reassign' === $reassignOption) {
86
+ $reassignUserId = gdpr('options')->get('delete_action_reassign_user');
87
+ } else {
88
+ $reassignUserId = false;
89
+ }
90
+
91
+ wp_delete_user($dataSubject->getUserId(), $reassignUserId);
92
+ }
93
+
94
+ public function anonymizeUser(DataSubject $dataSubject, $anonymizedId)
95
+ {
96
+ if (!$dataSubject->hasUser()) {
97
+ return;
98
+ }
99
+
100
+ if (!$anonymizedId) {
101
+ gdpr('helpers')->error();
102
+ }
103
+
104
+ // Save a unique identifier to tie anonymized data together for analytics purposes
105
+ update_user_meta($dataSubject->getUserId(), "gdpr_anonymized_id", $anonymizedId);
106
+
107
+ // Change username
108
+ global $wpdb;
109
+
110
+ $anonymizedUsername = apply_filters('gdpr/wordpress-user/anonymize/username', '[anonymous]');
111
+ $wpdb->update(
112
+ $wpdb->users,
113
+ ['user_login' => $anonymizedUsername],
114
+ ['ID' => $dataSubject->getUserId()]
115
+ );
116
+
117
+ // Clear all relevant user fields, reset password
118
+ wp_update_user([
119
+ 'ID' => $dataSubject->getUserId(),
120
+ 'user_email' => '',
121
+ 'user_nicename' => '',
122
+ 'user_url' => '',
123
+ 'user_activation_key' => '',
124
+ 'display_name' => $anonymizedUsername,
125
+ // Set a random password, just in case the functionality that disallows users from logging in should break for any reason
126
+ 'user_pass' => wp_hash_password(wp_generate_password()),
127
+ ]);
128
+
129
+ // Clear all relevant usermeta fields
130
+ delete_user_meta($dataSubject->getUserId(), 'first_name');
131
+ delete_user_meta($dataSubject->getUserId(), 'last_name');
132
+ delete_user_meta($dataSubject->getUserId(), 'nickname');
133
+ delete_user_meta($dataSubject->getUserId(), 'description');
134
+ delete_user_meta($dataSubject->getUserId(), 'session_tokens');
135
+ delete_user_meta($dataSubject->getUserId(), 'community-events-location');
136
+
137
+ // Remove all capabilities
138
+ $user = $dataSubject->getUser();
139
+ $user->remove_all_caps();
140
+
141
+ // Finally, assign the 'anonymous' role to user
142
+ if (apply_filters('gdpr/wordpress-user/anonymize/change_role', true) && get_role('anonymous')) {
143
+
144
+ foreach ($user->roles as $role) {
145
+ $user->remove_role($role);
146
+ }
147
+
148
+ $user->add_role('anonymous');
149
+ }
150
+ }
151
+ }
src/Components/WordpressUser/RegistrationForm.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\WordpressUser;
4
+
5
+ use Codelight\GDPR\DataSubject\DataSubjectManager;
6
+
7
+ class RegistrationForm
8
+ {
9
+ /* @var DataSubjectManager */
10
+ protected $dataSubjectManager;
11
+
12
+ public function __construct(DataSubjectManager $dataSubjectManager)
13
+ {
14
+ $this->dataSubjectManager = $dataSubjectManager;
15
+
16
+ if (gdpr('options')->get('policy_page')) {
17
+ add_action('register_form', [$this, 'addRegisterFormCheckbox']);
18
+ add_filter('registration_errors', [$this, 'validate'], PHP_INT_MAX);
19
+ }
20
+ }
21
+
22
+ public function addRegisterFormCheckbox()
23
+ {
24
+ $privacyPolicyUrl = get_permalink(gdpr('options')->get('policy_page'));
25
+ $termsPage = gdpr('options')->get('terms_page');
26
+
27
+ if ($termsPage) {
28
+ $termsUrl = get_permalink($termsPage);
29
+ } else {
30
+ $termsUrl = false;
31
+ }
32
+
33
+ echo gdpr('view')->render(
34
+ 'modules/wordpress-user/registration-terms-checkbox',
35
+ compact('privacyPolicyUrl', 'termsUrl')
36
+ );
37
+ }
38
+
39
+ public function validate(\WP_Error $errors)
40
+ {
41
+ if (empty($_POST['gdpr_terms']) || !$_POST['gdpr_terms']) {
42
+ $errors->add('gdpr_error', __('<strong>ERROR</strong>: You must accept the terms and conditions.', 'gdpr'));
43
+ } else {
44
+ $dataSubject = $this->dataSubjectManager->getByEmail($_POST['user_email']);
45
+ $dataSubject->giveConsent('privacy-policy');
46
+ }
47
+
48
+ return $errors;
49
+ }
50
+ }
src/Components/WordpressUser/WordpressUser.php ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Components\WordpressUser;
4
+
5
+ use Codelight\GDPR\DataSubject\DataSubjectManager;
6
+ use Codelight\GDPR\Components\WordpressUser\Controllers\DashboardDataPageController;
7
+ use Codelight\GDPR\Components\WordpressUser\Controllers\DashboardProfilePageController;
8
+
9
+ /**
10
+ * Handles everything related to a WordPress user account
11
+ *
12
+ * Class WordpressUser
13
+ *
14
+ * @package Codelight\GDPR\Modules\WordpressUser
15
+ */
16
+ class WordpressUser
17
+ {
18
+ /* @var string */
19
+ protected $name = 'wordpress-user';
20
+
21
+ /* @var DataManager */
22
+ protected $dataManager;
23
+
24
+ /* @var DataSubjectManager */
25
+ protected $dataSubjectManager;
26
+
27
+ /**
28
+ * WordpressUser constructor.
29
+ *
30
+ * @param DataSubjectManager $dataSubjectManager
31
+ * @param DataManager $dataManager
32
+ */
33
+ public function __construct(DataSubjectManager $dataSubjectManager, DataManager $dataManager)
34
+ {
35
+ $this->dataSubjectManager = $dataSubjectManager;
36
+ $this->dataManager = $dataManager;
37
+
38
+ gdpr()->make(DashboardProfilePageController::class);
39
+ gdpr()->make(RegistrationForm::class);
40
+
41
+ if (gdpr('options')->get('enable')) {
42
+ gdpr()->make(DashboardDataPageController::class);
43
+
44
+ // Register Privacy Tools page in admin
45
+ add_action('admin_menu', [$this, 'registerDashboardDataPage']);
46
+ }
47
+
48
+ // Register render action on Profile edit page
49
+ add_action('show_user_profile', [$this, 'triggerProfileRenderAction'], PHP_INT_MAX);
50
+ add_action('edit_user_profile', [$this, 'triggerProfileRenderAction'], PHP_INT_MAX);
51
+
52
+ add_filter('gdpr/data-subject/data', [$this, 'getExportData'], 1, 2);
53
+ add_action('gdpr/data-subject/delete', [$this, 'deleteUser'], 100);
54
+ add_action('gdpr/data-subject/anonymize', [$this, 'anonymizeUser'], 100, 2);
55
+ }
56
+
57
+ /**
58
+ * Register Privacy Tools dashboard page under Users
59
+ */
60
+ public function registerDashboardDataPage()
61
+ {
62
+ add_users_page(
63
+ __('Privacy Tools', 'gdpr-admin'),
64
+ __('Privacy Tools', 'gdpr-admin'),
65
+ 'read',
66
+ 'gdpr-profile',
67
+ [$this, 'renderDashboardDataPage']
68
+ );
69
+ }
70
+
71
+ /**
72
+ * Render the contents of Privacy Tools dashboard page
73
+ */
74
+ public function renderDashboardDataPage()
75
+ {
76
+ $dataSubject = $this->dataSubjectManager->getByLoggedInUser();
77
+
78
+ if ($dataSubject) {
79
+ do_action('gdpr/dashboard/privacy-tools/content', $dataSubject);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * On profile page, trigger an action with the same format as the Router provides
85
+ * so that we have consistency with the rest of the hooks.
86
+ */
87
+ public function triggerProfileRenderAction(\WP_User $user)
88
+ {
89
+ if (current_user_can('edit_users') || current_user_can('delete_users')) {
90
+ $dataSubject = $this->dataSubjectManager->getByEmail($user->user_email);
91
+ do_action("gdpr/dashboard/profile-page/content", $dataSubject);
92
+ }
93
+ }
94
+
95
+ public function getExportData($data, $email)
96
+ {
97
+ return $data + $this->dataManager->getData($this->dataSubjectManager->getByEmail($email));
98
+ }
99
+
100
+ public function deleteUser($email)
101
+ {
102
+ $dataSubject = $this->dataSubjectManager->getByEmail($email);
103
+ $this->dataManager->deleteUser($dataSubject);
104
+ }
105
+
106
+ public function anonymizeUser($email, $anonymizedId)
107
+ {
108
+ $dataSubject = $this->dataSubjectManager->getByEmail($email);
109
+ $this->dataManager->anonymizeUser($dataSubject, $anonymizedId);
110
+ }
111
+ }
src/Config.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR;
4
+
5
+ class Config extends \Illuminate\Config\Repository
6
+ {
7
+
8
+ }
src/Container.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR;
4
+
5
+ class Container extends \Illuminate\Container\Container
6
+ {
7
+ protected static $instance;
8
+ }
src/DataSubject/AdminTabDataSubject.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\DataSubject;
4
+
5
+ use Codelight\GDPR\Admin\AdminTab;
6
+
7
+ /**
8
+ * Class AdminTabDataSubject
9
+ *
10
+ * @package Codelight\GDPR\DataSubject
11
+ */
12
+ class AdminTabDataSubject extends AdminTab
13
+ {
14
+ /* @var string */
15
+ protected $slug = 'data-subject';
16
+
17
+ /* @var DataSubjectManager */
18
+ protected $dataSubjectManager;
19
+
20
+ /**
21
+ * AdminTabDataSubject constructor.
22
+ *
23
+ * @param DataSubjectManager $dataSubjectManager
24
+ */
25
+ public function __construct(DataSubjectManager $dataSubjectManager)
26
+ {
27
+ $this->title = __('Data Subjects', 'gdpr-admin');
28
+ $this->dataSubjectManager = $dataSubjectManager;
29
+
30
+ // Workaround to allow this page to be submitted
31
+ $this->registerSetting('gdpr_email');
32
+
33
+ // Register handler for this action
34
+ add_action('gdpr/admin/action/search', [$this, 'searchRedirect']);
35
+ }
36
+
37
+ public function init()
38
+ {
39
+ $this->registerSettingSection(
40
+ 'gdpr-section-data-subjects',
41
+ __('Data Subjects', 'gdpr-admin'),
42
+ [$this, 'renderTab']
43
+ );
44
+ }
45
+
46
+ public function renderTab()
47
+ {
48
+ if (isset($_GET['search']) && $_GET['search']) {
49
+ $results = $this->getRenderedResults($_GET['search'], $this->dataSubjectManager->getByEmail($_GET['search']));
50
+ } else {
51
+ $results = '';
52
+ }
53
+
54
+ $nonce = wp_create_nonce('gdpr/admin/action/search');
55
+ echo gdpr('view')->render(
56
+ 'admin/data-subjects/search-form',
57
+ compact('nonce', 'results', 'exportUrl', 'deleteUrl')
58
+ );
59
+ }
60
+
61
+ public function getRenderedResults($email, DataSubject $dataSubject)
62
+ {
63
+ $hasData = $dataSubject->hasData();
64
+ $links = [];
65
+
66
+ if ($hasData) {
67
+ if ($dataSubject->getUserId()) {
68
+ $userName = get_userdata($dataSubject->getUserId())->user_login;
69
+ $links['profile'] = get_edit_user_link($dataSubject->getUserId());
70
+ $adminCap = user_can($dataSubject->getUserId(), 'manage_options');
71
+
72
+ } else {
73
+ $userName = false;
74
+ $adminCap = false;
75
+ }
76
+
77
+ /**
78
+ * TODO: these actions are currently triggered in DashboardProfilePageController
79
+ * Should replace this with a generic AdminController!
80
+ * Also consider namespacing gdpr_action in this case, i.e. profile/delete vs data-subject-tab/delete
81
+ */
82
+ $links['view'] = add_query_arg([
83
+ 'gdpr_action' => 'export',
84
+ 'gdpr_format' => 'html',
85
+ 'gdpr_email' => $_GET['search'],
86
+ 'gdpr_nonce' => wp_create_nonce("gdpr/admin/action/export"),
87
+ ]);
88
+
89
+ $links['export'] = add_query_arg([
90
+ 'gdpr_action' => 'export',
91
+ 'gdpr_format' => 'json',
92
+ 'gdpr_email' => $_GET['search'],
93
+ 'gdpr_nonce' => wp_create_nonce("gdpr/admin/action/export"),
94
+ ]);
95
+
96
+ $links['anonymize'] = add_query_arg([
97
+ 'gdpr_email' => $_GET['search'],
98
+ 'gdpr_action' => 'forget',
99
+ 'gdpr_force_action' => 'anonymize',
100
+ 'gdpr_nonce' => wp_create_nonce("gdpr/admin/action/forget"),
101
+ ]);
102
+
103
+ $links['delete'] = add_query_arg([
104
+ 'gdpr_email' => $_GET['search'],
105
+ 'gdpr_action' => 'forget',
106
+ 'gdpr_force_action' => 'delete',
107
+ 'gdpr_nonce' => wp_create_nonce("gdpr/admin/action/forget"),
108
+ ]);
109
+ }
110
+
111
+ return gdpr('view')->render('admin/data-subjects/search-results', compact('email', 'hasData', 'links', 'userName', 'adminCap'));
112
+ }
113
+
114
+ public function renderSubmitButton()
115
+ {
116
+ // Intentionally left blank
117
+ }
118
+
119
+ public function searchRedirect()
120
+ {
121
+ if (isset($_POST['gdpr_email']) && $_POST['gdpr_email']) {
122
+ wp_safe_redirect(gdpr('helpers')->getAdminUrl('&gdpr-tab=data-subject&search=' . $_POST['gdpr_email']));
123
+ exit;
124
+ }
125
+ }
126
+ }
src/DataSubject/DataExporter.php ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\DataSubject;
4
+
5
+ /**
6
+ * Handle formatting and downloading data subject's data.
7
+ *
8
+ * Class DataManager
9
+ *
10
+ * @package Codelight\GDPR\DataSubject
11
+ */
12
+ class DataExporter
13
+ {
14
+ public function export(array $data, DataSubject $dataSubject, $format = 'html')
15
+ {
16
+ $data = $this->maybeUnserialize($data);
17
+
18
+ do_action('gdpr/export', $data, $dataSubject->getEmail(), $dataSubject, $format);
19
+
20
+ if ('html' === $format) {
21
+ $this->downloadHTML($data, $dataSubject);
22
+ } elseif ('json' === $format) {
23
+ $this->downloadJSON($data, $dataSubject);
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Download a data subject's data in human-readable format,
29
+ * formatted as a table in an HTML document unless overridden.
30
+ *
31
+ * @param array $data
32
+ * @param DataSubject $dataSubject
33
+ */
34
+ protected function downloadHTML(array $data, DataSubject $dataSubject)
35
+ {
36
+ // Allow extensions to send a different response
37
+ do_action('gdpr/export/html', $data, $dataSubject->getEmail(), $dataSubject);
38
+
39
+ $filename = 'data_' . date("Y-m-d_H:i:s") . '.html';
40
+
41
+ // By default, send a downloadable HTML file
42
+ header("Pragma: public");
43
+ header("Expires: 0");
44
+ header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
45
+ header("Cache-Control: private", false);
46
+ header("Content-Type: text/html");
47
+ header("Content-Disposition: attachment; filename=\"{$filename}\";");
48
+ header("Content-Transfer-Encoding: binary");
49
+
50
+ echo $this->getHtmlData($data);
51
+ exit;
52
+ }
53
+
54
+ /**
55
+ * Download a data subject's data in machine-readable format,
56
+ * formatted as JSON unless overridden.
57
+ *
58
+ * @param array $data
59
+ * @param DataSubject $dataSubject
60
+ */
61
+ protected function downloadJSON(array $data, DataSubject $dataSubject)
62
+ {
63
+ // Allow extensions to send a different response
64
+ do_action('gdpr/export/json', $data, $dataSubject->getEmail(), $dataSubject);
65
+
66
+ $filename = 'data_' . date("Y-m-d_H:i:s") . '.json';
67
+
68
+ // By default, encode to JSON and send a JSON response
69
+ header("Pragma: public");
70
+ header("Expires: 0");
71
+ header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
72
+ header("Cache-Control: private", false);
73
+ header("Content-Type: application/json");
74
+ header("Content-Disposition: attachment; filename=\"{$filename}\";");
75
+ header("Content-Transfer-Encoding: binary");
76
+
77
+ wp_send_json($data);
78
+ }
79
+
80
+ protected function getHtmlData($data)
81
+ {
82
+ $table = $this->formatAsTable($this->maybeUnserialize($data));
83
+ return gdpr('view')->render('global/html-data', compact('table'));
84
+ }
85
+
86
+ protected function formatAsTable(array $data, $level = 0)
87
+ {
88
+ $output = "<table class='level-{$level}'>";
89
+ foreach ($data as $key => $value) {
90
+ $output .= "<tr>";
91
+
92
+ // Output key
93
+ $output .= "<td class='key'>";
94
+ $output .= esc_html($key);
95
+ $output .= "</td>";
96
+
97
+ // Output value
98
+ $output .= "<td class='value'>";
99
+
100
+ // Account for arrays with just one item, such as usermeta
101
+ if (is_array($value) && 1 === count($value)) {
102
+ $value = $value[0];
103
+ }
104
+
105
+ // In case of arrays, recurse
106
+ if (is_array($value)) {
107
+ $output .= $this->formatAsTable($value, ($level + 1));
108
+ } else {
109
+ $output .= esc_html($value);
110
+ }
111
+ $output .= "</td>";
112
+
113
+ $output .= "</tr>";
114
+ }
115
+
116
+ $output .= "</table>";
117
+ return $output;
118
+ }
119
+
120
+ /**
121
+ * Recursively maybe unserialize data
122
+ *
123
+ * @param array $data
124
+ * @return array
125
+ */
126
+ protected function maybeUnserialize(array $data)
127
+ {
128
+ foreach ($data as &$datum) {
129
+ if (is_array($datum)) {
130
+ $datum = $this->maybeUnserialize($datum);
131
+ } else {
132
+ $datum = maybe_unserialize($datum);
133
+ }
134
+ }
135
+
136
+ return $data;
137
+ }
138
+ }
src/DataSubject/DataRepository.php ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\DataSubject;
4
+
5
+ /**
6
+ * Class DataRepository
7
+ *
8
+ * @package Codelight\GDPR\DataSubject
9
+ */
10
+ class DataRepository
11
+ {
12
+ /**
13
+ * DataRepository constructor.
14
+ *
15
+ * @param $email
16
+ */
17
+ public function __construct($email)
18
+ {
19
+ $this->email = $email;
20
+ }
21
+
22
+ /**
23
+ * Export all stored data. Triggers 'gdpr/data-subject/data' filter.
24
+ */
25
+ public function getData($email)
26
+ {
27
+ return apply_filters('gdpr/data-subject/data', [], $email);
28
+ }
29
+
30
+ /**
31
+ * Trigger the configured 'export' action
32
+ *
33
+ * @param $email
34
+ * @return array|null
35
+ */
36
+ public function export($email, $format, $force = false)
37
+ {
38
+ $action = gdpr('options')->get('export_action');
39
+ $data = null;
40
+
41
+ if ($force) {
42
+ $action = 'download';
43
+ }
44
+
45
+ switch($action) {
46
+ case 'download':
47
+ $data = $this->getData($email);
48
+ break;
49
+ case 'download_and_notify':
50
+ $data = $this->getData($email);
51
+ $this->notifyExportAction($email, $format);
52
+ break;
53
+ case 'notify':
54
+ $this->notifyExportRequest($email, $format);
55
+ break;
56
+ default:
57
+ $this->notifyExportRequest($email, $format);
58
+ break;
59
+ }
60
+
61
+ return $data;
62
+ }
63
+
64
+ /**
65
+ * Trigger the configured 'forget' action
66
+ *
67
+ * @param $email
68
+ *
69
+ * @return bool
70
+ */
71
+ public function forget($email, $forceAction = null)
72
+ {
73
+ $action = gdpr('options')->get('delete_action');
74
+
75
+ if ($forceAction) {
76
+ $action = $forceAction;
77
+ }
78
+
79
+ switch($action) {
80
+ case 'delete':
81
+ $this->delete($email);
82
+ return true;
83
+ case 'delete_and_notify':
84
+ $userId = $this->delete($email);
85
+ $this->notifyForgetAction($email, $userId);
86
+ return true;
87
+ case 'anonymize':
88
+ $this->anonymize($email);
89
+ return true;
90
+ case 'anonymize_and_notify':
91
+ $userId = $this->anonymize($email);
92
+ $this->notifyForgetAction($email, $userId);
93
+ return true;
94
+ case 'notify':
95
+ $this->notifyForgetRequest($email);
96
+ return false;
97
+ default:
98
+ $this->notifyForgetRequest($email);
99
+ return false;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * @param $email
105
+ */
106
+ protected function anonymize($email)
107
+ {
108
+ $userId = null;
109
+
110
+ if (email_exists($email)) {
111
+ $userId = get_user_by('email', $email)->ID;
112
+ }
113
+
114
+ $anonymizedId = wp_generate_password(12, false);
115
+ do_action('gdpr/data-subject/anonymize', $email, $anonymizedId, $userId);
116
+
117
+ return $userId;
118
+ }
119
+
120
+ /**
121
+ * @param $email
122
+ */
123
+ protected function delete($email)
124
+ {
125
+ $userId = null;
126
+
127
+ if (email_exists($email)) {
128
+ $userId = get_user_by('email', $email)->ID;
129
+ }
130
+
131
+ do_action('gdpr/data-subject/delete', $email, $userId);
132
+
133
+ return $userId;
134
+ }
135
+
136
+ /**
137
+ * @param $email
138
+ */
139
+ protected function notifyExportAction($email, $format)
140
+ {
141
+ wp_mail(
142
+ gdpr('options')->get('export_action_email'),
143
+ __("Data exported", 'gdpr'),
144
+ gdpr('view')->render('email/action-export', compact('email', 'format')),
145
+ ['Content-Type: text/html; charset=UTF-8']
146
+ );
147
+ }
148
+
149
+ /**
150
+ * @param $email
151
+ */
152
+ protected function notifyExportRequest($email, $format)
153
+ {
154
+ $adminTabLink = esc_url(gdpr('helpers')->getAdminUrl('&gdpr-tab=data-subject&search=' . $email));
155
+
156
+ wp_mail(
157
+ gdpr('options')->get('export_action_email'),
158
+ __("Data export request", 'gdpr'),
159
+ gdpr('view')->render('email/request-export', compact('email', 'format', 'adminTabLink')),
160
+ ['Content-Type: text/html; charset=UTF-8']
161
+ );
162
+ }
163
+
164
+ /**
165
+ * @param $email
166
+ */
167
+ protected function notifyForgetAction($email, $userId = null)
168
+ {
169
+ wp_mail(
170
+ gdpr('options')->get('delete_action_email'),
171
+ __("Data removed", 'gdpr'),
172
+ gdpr('view')->render('email/action-forget', compact('email', 'userId')),
173
+ ['Content-Type: text/html; charset=UTF-8']
174
+ );
175
+ }
176
+
177
+ /**
178
+ * @param $email
179
+ */
180
+ protected function notifyForgetRequest($email)
181
+ {
182
+ $adminTabLink = esc_url(gdpr('helpers')->getAdminUrl('&gdpr-tab=data-subject&search=' . $email));
183
+
184
+ wp_mail(
185
+ gdpr('options')->get('delete_action_email'),
186
+ __("Data removal request", 'gdpr'),
187
+ gdpr('view')->render('email/request-forget', compact('email', 'adminTabLink')),
188
+ ['Content-Type: text/html; charset=UTF-8']
189
+ );
190
+ }
191
+ }
src/DataSubject/DataSubject.php ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\DataSubject;
4
+
5
+ use Codelight\GDPR\Components\Consent\ConsentManager;
6
+
7
+ /**
8
+ * A data subject is any person whose data we are storing.
9
+ * A data subject might or might not have a user account on this site.
10
+ *
11
+ * Class DataSubject
12
+ *
13
+ * @package Codelight\GDPR\DataSubject
14
+ */
15
+ class DataSubject
16
+ {
17
+ /* @var string */
18
+ protected $email;
19
+
20
+ /* @var \WP_User|null */
21
+ protected $user = null;
22
+
23
+ /* @var ConsentManager */
24
+ protected $consentManager;
25
+
26
+ /* @var array */
27
+ protected $consents = [];
28
+
29
+ /**
30
+ * DataSubject constructor.
31
+ *
32
+ * @param $email
33
+ * @param \WP_User|null $user
34
+ */
35
+ public function __construct($email, \WP_User $user = null, ConsentManager $consentManager)
36
+ {
37
+ $this->email = $email;
38
+ $this->user = $user;
39
+ $this->consentManager = $consentManager;
40
+ $this->dataRepository = new DataRepository($email);
41
+
42
+ $this->consents = $this->consentManager->getAllConsents($this->email);
43
+ }
44
+
45
+ /**
46
+ * @return string
47
+ */
48
+ public function getEmail()
49
+ {
50
+ return $this->email;
51
+ }
52
+
53
+ /**
54
+ * @return bool
55
+ */
56
+ public function hasUser()
57
+ {
58
+ return $this->getUser() ? true : false;
59
+ }
60
+
61
+ /**
62
+ * @return null|\WP_User
63
+ */
64
+ public function getUser()
65
+ {
66
+ return $this->user;
67
+ }
68
+
69
+ /**
70
+ * @return int|null
71
+ */
72
+ public function getUserId()
73
+ {
74
+ if ($this->hasUser()) {
75
+ return $this->getUser()->ID;
76
+ }
77
+
78
+ return null;
79
+ }
80
+
81
+ /**
82
+ * Check if the data subject has consented to something specific
83
+ */
84
+ public function hasConsented($consent)
85
+ {
86
+ return in_array($consent, $this->consents);
87
+ }
88
+
89
+ /**
90
+ * Get all consent given by the data subject
91
+ */
92
+ public function getConsents()
93
+ {
94
+ return $this->consents;
95
+ }
96
+
97
+ /**
98
+ * Get a list of all consents intersected with the data subjects consents
99
+ */
100
+ public function getConsentData()
101
+ {
102
+ return $this->consentManager->getConsentData($this->consents);
103
+ }
104
+
105
+ /**
106
+ * Get a list of all visible consents
107
+ */
108
+ public function getVisibleConsentData()
109
+ {
110
+ return array_filter($this->getConsentData(), function ($item) {
111
+ return $item['visible'];
112
+ });
113
+ }
114
+
115
+ /**
116
+ * Save given consent to data subject
117
+ *
118
+ * @param $consent
119
+ */
120
+ public function giveConsent($consent)
121
+ {
122
+ $this->consentManager->giveConsent($this->email, $consent);
123
+ }
124
+
125
+ /**
126
+ * Remove given consent from data subject
127
+ *
128
+ * @param $consent
129
+ */
130
+ public function withdrawConsent($consent)
131
+ {
132
+ $this->consentManager->withdrawConsent($this->email, $consent);
133
+ }
134
+
135
+ /**
136
+ * Check if there's any data stored about this data subject at all
137
+ */
138
+ public function hasData()
139
+ {
140
+ return !empty($this->getData());
141
+ }
142
+
143
+ /**
144
+ * Just get the data subjects data, without sending emails or anything.
145
+ * Applies 'gdpr/data-subject/data' filter.
146
+ *
147
+ * @return array
148
+ */
149
+ public function getData()
150
+ {
151
+ return $this->dataRepository->getData($this->getEmail());
152
+ }
153
+
154
+ /**
155
+ * Export the data subject's data as per admin configuration.
156
+ * Applies 'gdpr/data-subject/data' filter.
157
+ *
158
+ * @return array|null
159
+ */
160
+ public function export($format, $force = false)
161
+ {
162
+ return $this->dataRepository->export($this->getEmail(), $format, $force);
163
+ }
164
+
165
+ /**
166
+ * Forget the data subject as per admin configuration.
167
+ * Triggers 'gdpr/data-subject/anonymize' or 'gdpr/data-subject/delete' action.
168
+ *
169
+ * @return bool
170
+ */
171
+ public function forget($forceAction = null)
172
+ {
173
+ return $this->dataRepository->forget($this->getEmail(), $forceAction);
174
+ }
175
+ }
src/DataSubject/DataSubjectAdmin.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\DataSubject;
4
+
5
+ class DataSubjectAdmin
6
+ {
7
+ public function __construct()
8
+ {
9
+ add_filter('gdpr/admin/tabs', [$this, 'registerTab'], 30);
10
+ }
11
+
12
+ public function registerTab($tabs)
13
+ {
14
+ $tabs['data-subject'] = gdpr()->make(AdminTabDataSubject::class);
15
+
16
+ return $tabs;
17
+ }
18
+ }
src/DataSubject/DataSubjectAuthenticator.php ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\DataSubject;
4
+
5
+ /**
6
+ * Handle authenticating the data subject either by logged in user or by email/cookie
7
+ *
8
+ * Class DataSubjectAuthenticator
9
+ *
10
+ * @package Codelight\GDPR\DataSubject
11
+ */
12
+ class DataSubjectAuthenticator
13
+ {
14
+ /**
15
+ * DataSubjectAuthenticator constructor.
16
+ *
17
+ * @param DataSubjectManager $dataSubjectManager
18
+ * @param DataSubjectIdentificator $dataSubjectIdentificator
19
+ */
20
+ public function __construct(DataSubjectManager $dataSubjectManager, DataSubjectIdentificator $dataSubjectIdentificator)
21
+ {
22
+ $this->dataSubjectManager = $dataSubjectManager;
23
+ $this->dataSubjectIdentificator = $dataSubjectIdentificator;
24
+ }
25
+
26
+ /**
27
+ * Attempt to authenticate the data subject
28
+ *
29
+ * @return bool|\Codelight\GDPR\DataSubject\DataSubject
30
+ */
31
+ public function authenticate()
32
+ {
33
+ // If the user is logged in, authenticate them
34
+ if (is_user_logged_in()) {
35
+ return apply_filters('gdpr/authenticate', $this->dataSubjectManager->getByLoggedInUser());
36
+ }
37
+
38
+ // If the request contains the identification cookie, validate it and identify the
39
+ // current user
40
+ $cookieData = $this->getIdentificationCookieData();
41
+ if ($cookieData && $this->dataSubjectIdentificator->isKeyValid($cookieData[0], $cookieData[1])) {
42
+ return apply_filters('gdpr/authenticate', $this->dataSubjectManager->getByEmail($cookieData[0]));
43
+ }
44
+
45
+ // Otherwise, we are not authenticated
46
+ return apply_filters('gdpr/authenticate', false);
47
+ }
48
+
49
+ /**
50
+ * If the request contains a new identification key, validate it, then set a new key
51
+ * to make the previous link obsolete.
52
+ */
53
+ public function identify()
54
+ {
55
+ // Do not attempt to identify logged in users
56
+ if (is_user_logged_in()) {
57
+ return;
58
+ }
59
+
60
+ if (isset($_REQUEST['gdpr_key']) && isset($_REQUEST['email'])) {
61
+
62
+ $privacyToolsPageUrl = get_permalink(gdpr('options')->get('tools_page'));
63
+
64
+ if ($this->dataSubjectIdentificator->isKeyValid($_REQUEST['email'], $_REQUEST['gdpr_key'])) {
65
+ $this->setIdentificationCookie($_REQUEST['email']);
66
+ $url = $privacyToolsPageUrl;
67
+ } else {
68
+ $url = add_query_arg([
69
+ 'gdpr_notice' => 'invalid_key',
70
+ ], $privacyToolsPageUrl);
71
+ }
72
+
73
+ wp_safe_redirect($url);
74
+ exit;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Set the identification cookie with the given key
80
+ *
81
+ * @param $key
82
+ */
83
+ public function setIdentificationCookie($email)
84
+ {
85
+ $key = $this->dataSubjectIdentificator->generateKey($email);
86
+
87
+ setcookie(
88
+ 'gdpr_key',
89
+ $email . '|' . $key,
90
+ time() + (15 * 60),
91
+ COOKIEPATH,
92
+ COOKIE_DOMAIN,
93
+ false,
94
+ true
95
+ );
96
+ }
97
+
98
+ /**
99
+ * @return string
100
+ */
101
+ public function getIdentificationCookieData()
102
+ {
103
+ return isset($_COOKIE['gdpr_key']) ? explode('|', $_COOKIE['gdpr_key']) : null;
104
+ }
105
+
106
+ /**
107
+ * Remove the cookie
108
+ */
109
+ public function deleteSession()
110
+ {
111
+ unset($_COOKIE['gdpr_key']);
112
+
113
+ setcookie(
114
+ 'gdpr_key',
115
+ '',
116
+ time() - 3600,
117
+ COOKIEPATH,
118
+ COOKIE_DOMAIN,
119
+ false,
120
+ true
121
+ );
122
+
123
+ wp_logout();
124
+ }
125
+ }
src/DataSubject/DataSubjectIdentificator.php ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\DataSubject;
4
+
5
+ use Codelight\GDPR\Options\Options;
6
+
7
+ /**
8
+ * Identify the data subject by unique temporary key
9
+ *
10
+ * Class DataSubjectIdentificator
11
+ *
12
+ * @package Codelight\GDPR\DataSubject
13
+ */
14
+ class DataSubjectIdentificator
15
+ {
16
+ /* @var DataSubjectManager */
17
+ protected $dataSubjectManager;
18
+
19
+ /* @var Options */
20
+ protected $options;
21
+
22
+ /**
23
+ * DataSubjectIdentificator constructor.
24
+ *
25
+ * @param DataSubjectManager $dataSubjectManager
26
+ */
27
+ public function __construct(DataSubjectManager $dataSubjectManager, Options $options)
28
+ {
29
+ $this->dataSubjectManager = $dataSubjectManager;
30
+ $this->options = $options;
31
+ }
32
+
33
+ /**
34
+ * Check if there is any data associated with the given email address
35
+ *
36
+ * @param $email
37
+ * @return bool
38
+ */
39
+ public function isDataSubject($email)
40
+ {
41
+ $dataSubject = $this->dataSubjectManager->getByEmail($email);
42
+
43
+ return apply_filters('gdpr/data-subject/has-data', $dataSubject->hasData(), $email);
44
+ }
45
+
46
+ /**
47
+ * Send the email with the link that allows data subject to authenticate
48
+ *
49
+ * @param $email
50
+ */
51
+ public function sendIdentificationEmail($email)
52
+ {
53
+ $key = $this->generateKey($email);
54
+ $privacyToolsPageUrl = gdpr('helpers')->getPrivacyToolsPageUrl();
55
+ $identificationUrl = add_query_arg([
56
+ 'gdpr_key' => $key,
57
+ 'email' => $email,
58
+ ], $privacyToolsPageUrl);
59
+
60
+ $siteName = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
61
+
62
+ // todo: handle or log email sending errors
63
+ wp_mail(
64
+ $email,
65
+ __("Your personal data on", 'gdpr') . ' ' . $siteName,
66
+ gdpr('view')->render('email/identify-data-subject', compact('identificationUrl', 'siteName')),
67
+ ['Content-Type: text/html; charset=UTF-8']
68
+ );
69
+ }
70
+
71
+ /**
72
+ * Notify the email address that we do not store any data about them
73
+ *
74
+ * @param $email
75
+ */
76
+ public function sendNoDataFoundEmail($email)
77
+ {
78
+ $siteName = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
79
+
80
+ wp_mail(
81
+ $email,
82
+ __("Your personal data on", 'gdpr') . ' ' . $siteName,
83
+ gdpr('view')->render('email/no-data', compact('siteName')),
84
+ ['Content-Type: text/html; charset=UTF-8']
85
+ );
86
+ }
87
+
88
+ /**
89
+ * Check if the given key is valid for the given email
90
+ *
91
+ * @param $email
92
+ * @param $key
93
+ * @return bool
94
+ */
95
+ public function isKeyValid($email, $key)
96
+ {
97
+ $keyData = $this->options->get("key_{$email}");
98
+
99
+ if (!$keyData) {
100
+ // No key exists
101
+ return false;
102
+ }
103
+
104
+ if (!isset($keyData['hashed-key']) || empty($keyData['hashed-key'])) {
105
+ // There was an error saving the data to database
106
+ return false;
107
+ }
108
+
109
+ if (!$this->validateKey($key, $keyData['hashed-key'])) {
110
+ // Invalid key
111
+ return false;
112
+ }
113
+
114
+ if ($keyData['valid-until'] < strtotime('now')) {
115
+ // expired key
116
+ return false;
117
+ }
118
+
119
+ // Double-check everything just to make sure we leave no errors in the code
120
+ return ($this->validateKey($key, $keyData['hashed-key']) && $keyData['valid-until'] > strtotime('now'));
121
+ }
122
+
123
+ /**
124
+ * Generate a secret key using the same functionality WP itself is using for Forgot Password requests
125
+ *
126
+ * @param $email
127
+ */
128
+ public function generateKey($email)
129
+ {
130
+ $key = wp_generate_password(20, false);
131
+ $this->saveKey($email, $key);
132
+
133
+ return $key;
134
+ }
135
+
136
+ /**
137
+ * Save key into the database along with the expiration timestamp
138
+ *
139
+ * @param $email
140
+ * @param $key
141
+ */
142
+ protected function saveKey($email, $key)
143
+ {
144
+ $this->options->set("key_{$email}", [
145
+ 'email' => $email,
146
+ 'hashed-key' => $this->hashKey($key),
147
+ 'valid-until' => strtotime('+15 minutes'),
148
+ ]);
149
+ }
150
+
151
+ /**
152
+ * @param $submittedKey
153
+ * @param $storedKey
154
+ */
155
+ protected function validateKey($submittedKey, $storedKey)
156
+ {
157
+ return $this->getHasher()->CheckPassword($submittedKey, $storedKey);
158
+ }
159
+
160
+ /**
161
+ * Hash the key before saving to database to keep it hidden from the prying eyes of your sysadmin
162
+ *
163
+ * @param $key
164
+ * @return bool|string
165
+ */
166
+ protected function hashKey($key)
167
+ {
168
+ return $this->getHasher()->HashPassword($key);
169
+ }
170
+
171
+ /**
172
+ * @return \PasswordHash
173
+ */
174
+ protected function getHasher()
175
+ {
176
+ global $wp_hasher;
177
+ if (empty($wp_hasher)) {
178
+ require_once ABSPATH . WPINC . '/class-phpass.php';
179
+ $wp_hasher = new \PasswordHash(8, true);
180
+ }
181
+
182
+ return $wp_hasher;
183
+ }
184
+ }
src/DataSubject/DataSubjectManager.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\DataSubject;
4
+
5
+ use Codelight\GDPR\Components\Consent\ConsentManager;
6
+
7
+ /**
8
+ * Handles finding data subjects by ID or email
9
+ *
10
+ * Class DataSubjectManager
11
+ *
12
+ * @package Codelight\GDPR\DataSubject
13
+ */
14
+ class DataSubjectManager
15
+ {
16
+ /* @var ConsentManager */
17
+ protected $consentManager;
18
+
19
+ /**
20
+ * DataSubjectManager constructor.
21
+ *
22
+ * @param ConsentManager $consentManager
23
+ */
24
+ public function __construct(ConsentManager $consentManager)
25
+ {
26
+ $this->consentManager = $consentManager;
27
+ }
28
+
29
+ /**
30
+ * @param $email
31
+ * @return DataSubject
32
+ */
33
+ public function getByEmail($email)
34
+ {
35
+ $user = get_user_by('email', $email);
36
+
37
+ return gdpr()->makeWith(
38
+ DataSubject::class,
39
+ [
40
+ 'email' => $email,
41
+ 'user' => $user ? $user : null,
42
+ 'consentManager' => $this->consentManager,
43
+ ]
44
+ );
45
+ }
46
+
47
+ /**
48
+ * @param $id
49
+ * @return DataSubject|false
50
+ */
51
+ public function getById($id)
52
+ {
53
+ $user = get_user_by('id', $id);
54
+
55
+ if (!$user) {
56
+ return false;
57
+ }
58
+
59
+ return gdpr()->makeWith(
60
+ DataSubject::class,
61
+ [
62
+ 'email' => $user->user_email,
63
+ 'user' => $user,
64
+ 'consentManager' => $this->consentManager,
65
+ ]
66
+ );
67
+ }
68
+
69
+ /**
70
+ * @return bool|DataSubject
71
+ */
72
+ public function getByLoggedInUser()
73
+ {
74
+ if (!is_user_logged_in()) {
75
+ return false;
76
+ }
77
+
78
+ return $this->getById(get_current_user_id());
79
+ }
80
+ }
src/Database/WordpressDatabase.php ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Database;
4
+
5
+ if (!defined('ABSPATH')) {
6
+ exit;
7
+ }
8
+
9
+ /**
10
+ * DB base class
11
+ * https://pippinsplugins.com/custom-database-api-the-basic-api-class/
12
+ */
13
+ abstract class WordpressDatabase
14
+ {
15
+
16
+ /**
17
+ * The name of our database table
18
+ *
19
+ * @access public
20
+ * @since 2.1
21
+ */
22
+ public $tableName;
23
+
24
+ /**
25
+ * The version of our database table
26
+ *
27
+ * @access public
28
+ * @since 2.1
29
+ */
30
+ public $version;
31
+
32
+ /**
33
+ * The name of the primary column
34
+ *
35
+ * @access public
36
+ * @since 2.1
37
+ */
38
+ public $primaryKey;
39
+
40
+ /**
41
+ * Get things started
42
+ *
43
+ * @access public
44
+ * @since 2.1
45
+ */
46
+ public function __construct()
47
+ {
48
+ }
49
+
50
+ /**
51
+ * Whitelist of columns
52
+ *
53
+ * @access public
54
+ * @since 2.1
55
+ * @return array
56
+ */
57
+ public function getColumns()
58
+ {
59
+ return [];
60
+ }
61
+
62
+ /**
63
+ * Default column values
64
+ *
65
+ * @access public
66
+ * @since 2.1
67
+ * @return array
68
+ */
69
+ public function getColumnDefaults()
70
+ {
71
+ return [];
72
+ }
73
+
74
+ /**
75
+ * Retrieve a row by the primary key
76
+ *
77
+ * @access public
78
+ * @since 2.1
79
+ * @return object
80
+ */
81
+ public function get($row_id)
82
+ {
83
+ global $wpdb;
84
+
85
+ return $wpdb->get_row($wpdb->prepare(
86
+ "SELECT * FROM $this->tableName WHERE $this->primary_key = %s LIMIT 1;", $row_id
87
+ ));
88
+ }
89
+
90
+ /**
91
+ * Retrieve a row by a specific column / value
92
+ *
93
+ * @access public
94
+ * @since 2.1
95
+ * @return object
96
+ */
97
+ public function getBy($column, $row_id)
98
+ {
99
+ global $wpdb;
100
+ $column = esc_sql($column);
101
+
102
+ return $wpdb->get_row($wpdb->prepare(
103
+ "SELECT * FROM $this->tableName WHERE $column = %s LIMIT 1;", $row_id
104
+ ));
105
+ }
106
+
107
+ /**
108
+ * Retrieve a specific column's value by the primary key
109
+ *
110
+ * @access public
111
+ * @since 2.1
112
+ * @return string
113
+ */
114
+ public function getColumn($column, $row_id)
115
+ {
116
+ global $wpdb;
117
+ $column = esc_sql($column);
118
+
119
+ return $wpdb->get_var($wpdb->prepare(
120
+ "SELECT $column FROM $this->tableName WHERE $this->primary_key = %s LIMIT 1;", $row_id
121
+ ));
122
+ }
123
+
124
+ /**
125
+ * Retrieve a specific column's value by the the specified column / value
126
+ *
127
+ * @access public
128
+ * @since 2.1
129
+ * @return string
130
+ */
131
+ public function getColumnBy($column, $column_where, $column_value)
132
+ {
133
+ global $wpdb;
134
+ $column_where = esc_sql($column_where);
135
+ $column = esc_sql($column);
136
+
137
+ return $wpdb->get_var($wpdb->prepare(
138
+ "SELECT $column FROM $this->tableName WHERE $column_where = %s LIMIT 1;", $column_value
139
+ ));
140
+ }
141
+
142
+ /**
143
+ * Insert a new row
144
+ *
145
+ * @access public
146
+ * @since 2.1
147
+ * @return int
148
+ */
149
+ public function insert($data, $type = '')
150
+ {
151
+ global $wpdb;
152
+
153
+ // Set default values
154
+ $data = wp_parse_args($data, $this->getColumnDefaults());
155
+
156
+ do_action('bs_db_pre_insert_' . $type, $data);
157
+
158
+ // Initialise column format array
159
+ $columnFormats = $this->getColumns();
160
+
161
+ // Force fields to lower case
162
+ $data = array_change_key_case($data);
163
+
164
+ // White list columns
165
+ $data = array_intersect_key($data, $columnFormats);
166
+
167
+ // Reorder $columnFormats to match the order of columns given in $data
168
+ $data_keys = array_keys($data);
169
+ $columnFormats = array_merge(array_flip($data_keys), $columnFormats);
170
+
171
+ $wpdb->insert($this->tableName, $data, $columnFormats);
172
+
173
+ do_action('bs_db_post_insert_' . $type, $wpdb->insert_id, $data);
174
+
175
+ return $wpdb->insert_id;
176
+ }
177
+
178
+ /**
179
+ * Update a row
180
+ *
181
+ * @access public
182
+ * @since 2.1
183
+ * @return bool
184
+ */
185
+ public function update($row_id, $data = [], $where = '')
186
+ {
187
+ global $wpdb;
188
+
189
+ // Row ID must be positive integer
190
+ $row_id = absint($row_id);
191
+
192
+ if (empty($row_id)) {
193
+ return false;
194
+ }
195
+
196
+ if (empty($where)) {
197
+ $where = $this->primaryKey;
198
+ }
199
+
200
+ // Initialise column format array
201
+ $columnFormats = $this->getColumns();
202
+
203
+ // Force fields to lower case
204
+ $data = array_change_key_case($data);
205
+
206
+ // White list columns
207
+ $data = array_intersect_key($data, $columnFormats);
208
+
209
+ // Reorder $columnFormats to match the order of columns given in $data
210
+ $data_keys = array_keys($data);
211
+ $columnFormats = array_merge(array_flip($data_keys), $columnFormats);
212
+
213
+ if (false === $wpdb->update($this->tableName, $data, [$where => $row_id], $columnFormats)) {
214
+ return false;
215
+ }
216
+
217
+ return true;
218
+ }
219
+
220
+
221
+ /**
222
+ * Delete a row identified by the primary key
223
+ *
224
+ * @access public
225
+ * @since 2.1
226
+ * @return bool
227
+ */
228
+ public function delete($row_id = 0)
229
+ {
230
+ global $wpdb;
231
+
232
+ // Row ID must be positive integer
233
+ $row_id = absint($row_id);
234
+
235
+ if (empty($row_id)) {
236
+ return false;
237
+ }
238
+
239
+ if (false === $wpdb->query($wpdb->prepare(
240
+ "DELETE FROM $this->tableName WHERE $this->primary_key = %d", $row_id
241
+ ))) {
242
+ return false;
243
+ }
244
+
245
+ return true;
246
+ }
247
+
248
+ /**
249
+ * Check if the given table exists
250
+ *
251
+ * @since 2.4
252
+ * @param string $table The table name
253
+ * @return bool If the table name exists
254
+ */
255
+ public function tableExists($table)
256
+ {
257
+ global $wpdb;
258
+ $table = sanitize_text_field($table);
259
+
260
+ return $wpdb->get_var($wpdb->prepare(
261
+ "SHOW TABLES LIKE '%s'", $table
262
+ )) === $table;
263
+ }
264
+
265
+
266
+ }
src/Helpers.php ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR;
4
+
5
+ /**
6
+ * General helper functions
7
+ *
8
+ * Class Helpers
9
+ *
10
+ * @package Codelight\GDPR
11
+ */
12
+ class Helpers
13
+ {
14
+ public function supportUrl($url = '')
15
+ {
16
+ return gdpr('config')->get('help.url') . $url;
17
+ }
18
+
19
+ /**
20
+ * Get an associative array of EU countries
21
+ *
22
+ * @return array
23
+ */
24
+ public function getEUCountryList()
25
+ {
26
+ return [
27
+ 'AT' => __('Austria', 'gdpr-admin'),
28
+ 'BE' => __('Belgium', 'gdpr-admin'),
29
+ 'BG' => __('Bulgaria', 'gdpr-admin'),
30
+ 'HR' => __('Croatia', 'gdpr-admin'),
31
+ 'CY' => __('Cyprus', 'gdpr-admin'),
32
+ 'CZ' => __('Czech Republic', 'gdpr-admin'),
33
+ 'DK' => __('Denmark', 'gdpr-admin'),
34
+ 'EE' => __('Estonia', 'gdpr-admin'),
35
+ 'FI' => __('Finland', 'gdpr-admin'),
36
+ 'FR' => __('France', 'gdpr-admin'),
37
+ 'DE' => __('Germany', 'gdpr-admin'),
38
+ 'GR' => __('Greece', 'gdpr-admin'),
39
+ 'HU' => __('Hungary', 'gdpr-admin'),
40
+ 'IE' => __('Ireland', 'gdpr-admin'),
41
+ 'IT' => __('Italy', 'gdpr-admin'),
42
+ 'LV' => __('Latvia', 'gdpr-admin'),
43
+ 'LT' => __('Lithuania', 'gdpr-admin'),
44
+ 'LU' => __('Luxembourg', 'gdpr-admin'),
45
+ 'MT' => __('Malta', 'gdpr-admin'),
46
+ 'NL' => __('Netherlands', 'gdpr-admin'),
47
+ 'PL' => __('Poland', 'gdpr-admin'),
48
+ 'PT' => __('Portugal', 'gdpr-admin'),
49
+ 'RO' => __('Romania', 'gdpr-admin'),
50
+ 'SK' => __('Slovakia', 'gdpr-admin'),
51
+ 'SI' => __('Slovenia', 'gdpr-admin'),
52
+ 'ES' => __('Spain', 'gdpr-admin'),
53
+ 'SE' => __('Sweden', 'gdpr-admin'),
54
+ 'UK' => __('United Kingdom', 'gdpr-admin'),
55
+ ];
56
+ }
57
+
58
+ /**
59
+ * Get a list of <option> values for the country selector
60
+ *
61
+ * @param null $current
62
+ *
63
+ * @return mixed
64
+ */
65
+ public function getCountrySelectOptions($current = null)
66
+ {
67
+ $eu = $this->getEUCountryList();
68
+ $outside = [
69
+ "IS" => __('Iceland', 'gdpr-admin'),
70
+ "NO" => __('Norway', 'gdpr-admin'),
71
+ "LI" => __('Liechtenstein', 'gdpr-admin'),
72
+ "CH" => __('Switzerland', 'gdpr-admin'),
73
+ "US" => __('United States', 'gdpr-admin'),
74
+ "other" => __('Rest of the world', 'gdpr-admin'),
75
+ ];
76
+
77
+ return gdpr('view')->render('global/country-options', compact('eu', 'outside', 'current'));
78
+ }
79
+
80
+ /**
81
+ * Check if a controller from the given country needs a representative in the EU
82
+ *
83
+ * @param $code
84
+ * @return bool
85
+ */
86
+ public function countryNeedsRepresentative($code)
87
+ {
88
+ return in_array($code, ['US', 'other']);
89
+ }
90
+
91
+ /**
92
+ * Get the data protection authority information for a given country
93
+ *
94
+ * @param null $countryCode
95
+ * @return array
96
+ */
97
+ public function getDataProtectionAuthorityInfo($countryCode = null)
98
+ {
99
+ if (!$countryCode) {
100
+ $countryCode = gdpr('options')->get('company_location');
101
+ }
102
+
103
+ $dpaData = require(gdpr('config')->get('plugin.path') . 'assets/data-protection-authorities.php');
104
+
105
+ if (isset($dpaData[$countryCode])) {
106
+ return $dpaData[$countryCode];
107
+ }
108
+
109
+ return [];
110
+ }
111
+
112
+ /**
113
+ * Get the info regarding all DPAs
114
+ */
115
+ public function getDataProtectionAuthorities()
116
+ {
117
+ return require(gdpr('config')->get('plugin.path') . 'assets/data-protection-authorities.php');
118
+ }
119
+
120
+ public function getAdminUrl($suffix = '')
121
+ {
122
+ return admin_url('tools.php?page=privacy' . $suffix);
123
+ }
124
+
125
+ public function getDashboardDataPageUrl($suffix = '')
126
+ {
127
+ return admin_url('users.php?page=gdpr-profile' . $suffix);
128
+ }
129
+
130
+ public function getPrivacyToolsPageUrl()
131
+ {
132
+ $toolsPageId = gdpr('options')->get('tools_page');
133
+ return $toolsPageId ? get_permalink($toolsPageId) : '';
134
+ }
135
+
136
+ public function getPrivacyPolicyPageUrl()
137
+ {
138
+ $policyPageId = gdpr('options')->get('policy_page');
139
+ return $policyPageId ? get_permalink($policyPageId) : '';
140
+ }
141
+
142
+ public function error()
143
+ {
144
+ wp_die(
145
+ __('An error has occurred. Please contact the site administrator.', 'gdpr')
146
+ );
147
+ }
148
+
149
+ public function docs($url = '')
150
+ {
151
+ return 'https://codelight.eu/wordpress-gdpr-framework/' . $url;
152
+ }
153
+ }
src/Installer/AdminInstallerNotice.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Installer;
4
+
5
+ use Codelight\GDPR\Admin\AdminNotice;
6
+
7
+ class AdminInstallerNotice extends AdminNotice
8
+ {
9
+ public function render()
10
+ {
11
+ if (!$this->template) {
12
+ trigger_error('Template not set for installer step admin notice!', E_USER_ERROR);
13
+ }
14
+
15
+ echo gdpr('view')->render('admin/notices/header-step');
16
+ echo gdpr('view')->render($this->template, $this->data);
17
+ echo gdpr('view')->render('admin/notices/footer-step');
18
+ }
19
+ }
src/Installer/Installer.php ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Installer;
4
+
5
+ use Codelight\GDPR\Admin\AdminTabGeneral;
6
+
7
+ /**
8
+ * Handle all installation activities
9
+ *
10
+ * Class Installer
11
+ *
12
+ * @package Codelight\GDPR\Installer
13
+ */
14
+ class Installer
15
+ {
16
+ /* @var array */
17
+ protected $defaultSteps = [
18
+ 'Codelight\GDPR\Installer\Steps\Welcome',
19
+ 'Codelight\GDPR\Installer\Steps\Disclaimer',
20
+ 'Codelight\GDPR\Installer\Steps\ConfigurationPages',
21
+ 'Codelight\GDPR\Installer\Steps\ConfigurationSettings',
22
+ 'Codelight\GDPR\Installer\Steps\PolicySettings',
23
+ 'Codelight\GDPR\Installer\Steps\PolicyContents',
24
+ 'Codelight\GDPR\Installer\Steps\Consent',
25
+ 'Codelight\GDPR\Installer\Steps\Integrations',
26
+ 'Codelight\GDPR\Installer\Steps\Finish',
27
+ ];
28
+
29
+ /* @var array */
30
+ protected $steps = [];
31
+
32
+ /* @var InstallerWizard */
33
+ protected $wizard;
34
+
35
+ /* @var InstallerRouter */
36
+ protected $router;
37
+
38
+ /**
39
+ * Check if the installer is enabled and ensure the user has correct permissions to run it
40
+ */
41
+ public function __construct(AdminTabGeneral $adminTab)
42
+ {
43
+ if (!$this->isInstallerEnabled()) {
44
+ return;
45
+ }
46
+
47
+ if (!$this->userHasPermissions()) {
48
+ return;
49
+ }
50
+
51
+ $this->adminTab = $adminTab;
52
+
53
+ $this->maybeDisplayDisclaimer();
54
+ $this->setupHooks();
55
+
56
+ if (!$this->isInstalled()) {
57
+ $this->setupSteps();
58
+ $this->runInstaller();
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Setup actions and admin tab components
64
+ */
65
+ protected function setupHooks()
66
+ {
67
+ add_action('admin_init', [$this, 'setupAdminGeneralTabButtons'], 0);
68
+
69
+ add_action('gdpr/admin/action/accept_disclaimer', [$this, 'acceptDisclaimer']);
70
+
71
+ add_action('gdpr/admin/action/restart_wizard', [$this, 'restartWizard']);
72
+
73
+ add_action('gdpr/admin/action/auto_install', [$this, 'autoInstall']);
74
+ add_action('gdpr/admin/action/skip_install', [$this, 'skipInstall']);
75
+ }
76
+
77
+ protected function runInstaller()
78
+ {
79
+ $this->wizard = new InstallerWizard;
80
+ $this->router = new InstallerRouter($this->steps);
81
+
82
+ // If we're currently on one of the installer steps, let the router handle it
83
+ if ($this->router->isInstallerStep()) {
84
+ return;
85
+ }
86
+
87
+ if ($this->getCurrentStepSlug()) {
88
+ // If the current step is set, display continue notice
89
+ $step = $this->router->findStep($this->getCurrentStepSlug());
90
+ // If step doesn't exist, then it means the step slugs have changed. Do nothing.
91
+ if (!$step) {
92
+ return;
93
+ }
94
+ $this->displayContinueNotice($step->getUrl());
95
+ } else {
96
+ // If the current step is not set, it means the installer hasn't been started yet
97
+ $this->displayWelcomeNotice();
98
+ }
99
+ }
100
+
101
+ /**
102
+ * If the admin has not accepted the disclaimer, render it
103
+ */
104
+ public function maybeDisplayDisclaimer()
105
+ {
106
+ if (!gdpr('options')->get('plugin_disclaimer_accepted') && (isset($_GET['page']) && 'privacy' === $_GET['page'])) {
107
+ $acceptUrl = add_query_arg([
108
+ 'gdpr_action' => 'accept_disclaimer',
109
+ 'gdpr_nonce' => wp_create_nonce('gdpr/admin/action/accept_disclaimer'),
110
+ ]);
111
+ gdpr('admin-notice')->add('admin/notices/disclaimer', compact('acceptUrl'));
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Mark the disclaimer as accepted
117
+ */
118
+ public function acceptDisclaimer()
119
+ {
120
+ gdpr('options')->set('plugin_disclaimer_accepted', 'yes');
121
+ wp_safe_redirect(gdpr('helpers')->getAdminUrl());
122
+ exit;
123
+ }
124
+
125
+ /**
126
+ * Display installer section in admin page
127
+ */
128
+ public function setupAdminGeneralTabButtons()
129
+ {
130
+ /**
131
+ * Display wizard buttons
132
+ */
133
+ $this->adminTab->registerSettingSection(
134
+ 'gdpr-section-wizard',
135
+ __('Setup Wizard', 'gdpr-admin'),
136
+ [$this, 'renderWizardButtons']
137
+ );
138
+ }
139
+
140
+ /**
141
+ * Render the installer section
142
+ */
143
+ public function renderWizardButtons()
144
+ {
145
+ $restartUrl = add_query_arg([
146
+ 'gdpr_action' => 'restart_wizard',
147
+ 'gdpr_nonce' => wp_create_nonce("gdpr/admin/action/restart_wizard"),
148
+ ]);
149
+
150
+ echo gdpr('view')->render(
151
+ 'admin/wizard-buttons',
152
+ compact('restartUrl')
153
+ );
154
+ }
155
+
156
+ /**
157
+ * Restart and redirect to first step
158
+ */
159
+ public function restartWizard()
160
+ {
161
+ gdpr('options')->delete('installer_step');
162
+ gdpr('options')->delete('is_installed');
163
+
164
+ wp_safe_redirect(self_admin_url());
165
+ exit;
166
+ }
167
+
168
+ /**
169
+ * Allow plugins to modify the steps
170
+ */
171
+ protected function setupSteps()
172
+ {
173
+ $steps = apply_filters('gdpr/installer/steps', $this->defaultSteps);
174
+
175
+ foreach ($steps as $index => $step) {
176
+ $this->steps[$index] = new $step;
177
+ }
178
+ }
179
+
180
+ /**
181
+ * The installer can be disabled by filter.
182
+ * Check if it's enabled
183
+ *
184
+ * @return bool
185
+ */
186
+ protected function isInstallerEnabled()
187
+ {
188
+ return apply_filters('gdpr/installer/enabled', true);
189
+ }
190
+
191
+ /**
192
+ * Check if the current user has correct permissions to run the installer
193
+ *
194
+ * @return bool
195
+ */
196
+ protected function userHasPermissions()
197
+ {
198
+ return current_user_can(apply_filters('gdpr/installer/permissions', 'manage_options'));
199
+ }
200
+
201
+ /**
202
+ * Check if the installer is already ran
203
+ *
204
+ * @return bool
205
+ */
206
+ protected function isInstalled()
207
+ {
208
+ return gdpr('options')->get('is_installed');
209
+ }
210
+
211
+ /**
212
+ * @return string
213
+ */
214
+ public function getCurrentStepSlug()
215
+ {
216
+ return gdpr('options')->get('installer_step');
217
+ }
218
+
219
+ /**
220
+ * Render an admin notice that will display the welcome message
221
+ */
222
+ protected function displayWelcomeNotice()
223
+ {
224
+ // Make sure we display the notice only to admins
225
+ if (!current_user_can(apply_filters('gdpr/capability', 'manage_options'))) {
226
+ return;
227
+ }
228
+
229
+ $installerUrl = $this->steps[0]->getUrl();
230
+ $autoInstallUrl = add_query_arg([
231
+ 'gdpr_action' => 'auto_install',
232
+ 'gdpr_nonce' => wp_create_nonce("gdpr/admin/action/auto_install"),
233
+ ]);
234
+ $skipUrl = add_query_arg([
235
+ 'gdpr_action' => 'skip_install',
236
+ 'gdpr_nonce' => wp_create_nonce("gdpr/admin/action/skip_install"),
237
+ ]);
238
+
239
+ gdpr('admin-notice')->add(
240
+ 'installer/welcome-notice',
241
+ compact('installerUrl', 'autoInstallUrl', 'skipUrl')
242
+ );
243
+ }
244
+
245
+ /**
246
+ * Render an admin notice that will display the continue button
247
+ *
248
+ * @param $url
249
+ */
250
+ protected function displayContinueNotice($url)
251
+ {
252
+ // Make sure we display the notice only to admins
253
+ if (!current_user_can(apply_filters('gdpr/capability', 'manage_options'))) {
254
+ return;
255
+ }
256
+
257
+ $skipUrl = add_query_arg([
258
+ 'gdpr_action' => 'skip_install',
259
+ 'gdpr_nonce' => wp_create_nonce("gdpr/admin/action/skip_install"),
260
+ ]);
261
+
262
+ gdpr('admin-notice')->add('installer/continue-notice', ['buttonUrl' => $url, 'skipUrl' => $skipUrl]);
263
+ }
264
+
265
+ /**
266
+ * Automatically create pages for Privacy Policy and set the corresponding options
267
+ */
268
+ public function autoInstall()
269
+ {
270
+ $policyPageId = wp_insert_post([
271
+ 'post_title' => __('Privacy Policy', 'gdpr'),
272
+ 'post_type' => 'page',
273
+ ]);
274
+
275
+ gdpr('options')->set('policy_page', $policyPageId);
276
+
277
+ $toolsPageId = wp_insert_post([
278
+ 'post_content' => '[gdpr_privacy_tools]',
279
+ 'post_title' => __('Privacy Tools', 'gdpr'),
280
+ 'post_type' => 'page',
281
+ ]);
282
+ gdpr('options')->set('tools_page', $toolsPageId);
283
+
284
+ // Woocommerce compatibility - automatically add their terms page
285
+ if (get_option('woocommerce_terms_page_id')) {
286
+ gdpr('options')->set('terms_page', get_option('woocommerce_terms_page_id'));
287
+ }
288
+
289
+ gdpr('options')->set('is_installed', 'yes');
290
+ wp_safe_redirect(gdpr('helpers')->getAdminUrl('&gdpr-tab=privacy-policy&gdpr-notice=autoinstall'));
291
+ exit;
292
+ }
293
+
294
+ /**
295
+ * Do nothing, but mark the installer as completed
296
+ */
297
+ public function skipInstall()
298
+ {
299
+ gdpr('options')->set('is_installed', 'yes');
300
+ wp_safe_redirect(gdpr('helpers')->getAdminUrl());
301
+ exit;
302
+ }
303
+ }
src/Installer/InstallerRouter.php ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Installer;
4
+
5
+ /**
6
+ * Handle redirecting and routing the installer steps
7
+ *
8
+ * Class InstallerRouter
9
+ *
10
+ * @package Codelight\GDPR\Installer
11
+ */
12
+ class InstallerRouter
13
+ {
14
+ /**
15
+ * Set up the router
16
+ *
17
+ * @param array $steps
18
+ */
19
+ public function __construct(array $steps)
20
+ {
21
+ $this->steps = $steps;
22
+
23
+ if (isset($_GET['gdpr-step'])) {
24
+ add_action('admin_init', [$this, 'route']);
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Do the magic
30
+ */
31
+ public function route()
32
+ {
33
+ /* @var $step InstallerStepInterface */
34
+ $step = $this->findStep($_GET['gdpr-step']);
35
+
36
+ if (!$step) {
37
+ trigger_error("Step {$_GET['gdpr-step']} not found!", E_USER_ERROR);
38
+ }
39
+
40
+ if ('POST' === $_SERVER['REQUEST_METHOD'] && isset($_POST['gdpr-installer'])) {
41
+
42
+ // Handle the previous step button
43
+ if ('previous' === $_POST['gdpr-installer']) {
44
+ $this->setCurrentStep($this->getPreviousStep($step)->getSlug());
45
+ wp_safe_redirect($this->getPreviousStep($step)->getUrl(), 302);
46
+ exit;
47
+ }
48
+
49
+ // Handle form submission
50
+ if ('next' === $_POST['gdpr-installer']) {
51
+
52
+ if (!$step->validateNonce()) {
53
+ wp_safe_redirect($step->getUrl() . '&gdpr-error=nonce', 302);
54
+ exit;
55
+ }
56
+
57
+ // Handle successful validation
58
+ if ($step->validate()) {
59
+ $step->submit();
60
+ $nextStep = $this->getNextStep($step);
61
+ if ($nextStep) {
62
+ $this->setCurrentStep($nextStep->getSlug());
63
+ wp_safe_redirect($nextStep->getUrl(), 302);
64
+ exit;
65
+ } else {
66
+ // If no next step then we're done
67
+ wp_safe_redirect(admin_url(), 302);
68
+ exit;
69
+ }
70
+ }
71
+
72
+ // Handle form submission with failed validation
73
+ wp_safe_redirect($step->getUrl() . '&gdpr-error=' . $step->getErrors(), 302);
74
+ exit;
75
+ }
76
+
77
+ trigger_error('Installer action not defined!', E_USER_NOTICE);
78
+
79
+ } else {
80
+
81
+ // Run wizard page step
82
+ if ('wizard' === $step->getType()) {
83
+ ob_start();
84
+ $step->run();
85
+ exit;
86
+ }
87
+
88
+ // Run regular admin page step
89
+ $step->run();
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Find a step by slug
95
+ *
96
+ * @param $slug
97
+ * @return InstallerStepInterface|bool
98
+ */
99
+ public function findStep($slug)
100
+ {
101
+ foreach ($this->steps as $i => $step) {
102
+ if ($slug === $step->getSlug()) {
103
+ return $step;
104
+ }
105
+ }
106
+
107
+ return false;
108
+ }
109
+
110
+ /**
111
+ * Find the index of a step by its slug
112
+ *
113
+ * @param $slug
114
+ * @return bool|int|string
115
+ */
116
+ protected function findStepIndex($slug)
117
+ {
118
+ foreach ($this->steps as $i => $step) {
119
+ if ($slug === $step->getSlug()) {
120
+ return $i;
121
+ }
122
+ }
123
+
124
+ return false;
125
+ }
126
+
127
+ /**
128
+ * Get the URL of the previous step or false if it's the first step
129
+ *
130
+ * @param InstallerStepInterface $step
131
+ * @return InstallerStepInterface|bool
132
+ */
133
+ public function getPreviousStep(InstallerStepInterface $step)
134
+ {
135
+ $currentStepNumber = $this->findStepIndex($step->getSlug());
136
+
137
+ if (false === $currentStepNumber or 0 === $currentStepNumber) {
138
+ $prevStep = false;
139
+ } else {
140
+ $prevStep = $this->steps[$currentStepNumber - 1];
141
+ }
142
+
143
+ return apply_filters('gdpr/installer/prev-step', $prevStep, $currentStepNumber);
144
+ }
145
+
146
+ /**
147
+ * Get the URL of the next step or admin dashboard if it's the last step
148
+ *
149
+ * @param InstallerStepInterface $step
150
+ * @return InstallerStepInterface|bool
151
+ */
152
+ public function getNextStep(InstallerStepInterface $step)
153
+ {
154
+ $currentStepNumber = $this->findStepIndex($step->getSlug());
155
+
156
+ if (false === $currentStepNumber or count($this->steps) === $currentStepNumber + 1) {
157
+ $nextStep = false;
158
+ } else {
159
+ $nextStep = $this->steps[$currentStepNumber + 1];
160
+ }
161
+
162
+ return apply_filters('gdpr/installer/next-step-url', $nextStep, $currentStepNumber);
163
+ }
164
+
165
+ /**
166
+ * Save the current step slug in the database to enable continue functionality
167
+ *
168
+ * @param $steps
169
+ */
170
+ protected function setCurrentStep($slug)
171
+ {
172
+ gdpr('options')->set('installer_step', $slug);
173
+ }
174
+
175
+ /**
176
+ * Check if we are currently on any of the installer steps
177
+ *
178
+ * @return array|bool
179
+ */
180
+ public function isInstallerStep()
181
+ {
182
+ return isset($_GET['gdpr-step']) && $this->findStep($_GET['gdpr-step']);
183
+ }
184
+ }
src/Installer/InstallerStep.php ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Installer;
4
+
5
+ /**
6
+ * Handle the plumbing of an installer step
7
+ *
8
+ * Class InstallerStep
9
+ *
10
+ * @package Codelight\GDPR\Installer
11
+ */
12
+ abstract class InstallerStep
13
+ {
14
+ /* @var string */
15
+ protected $stepType;
16
+
17
+ /* @var string */
18
+ protected $slug;
19
+
20
+ /* @var string */
21
+ protected $type;
22
+
23
+ /* @var string */
24
+ protected $template;
25
+
26
+ /* @var int */
27
+ protected $activeSteps;
28
+
29
+ /**
30
+ * Render a step for viewing
31
+ */
32
+ public function run()
33
+ {
34
+ $this->enqueue();
35
+ $this->renderHeader();
36
+ $this->renderContent();
37
+ $this->renderNonce();
38
+ $this->renderFooter();
39
+ }
40
+
41
+ /**
42
+ * Validate the form submission
43
+ *
44
+ * @return bool
45
+ */
46
+ public function validate()
47
+ {
48
+ return true;
49
+ }
50
+
51
+ /**
52
+ * Validate the nonce
53
+ *
54
+ * @return bool
55
+ */
56
+ public function validateNonce()
57
+ {
58
+ return isset($_POST['gdpr_nonce']) && wp_verify_nonce($_POST['gdpr_nonce'], $this->slug);
59
+ }
60
+
61
+ /**
62
+ * Process the form submission
63
+ */
64
+ public function submit()
65
+ {
66
+
67
+ }
68
+
69
+
70
+ /**
71
+ * Display error notice or something
72
+ */
73
+ public function getErrors()
74
+ {
75
+ return $this->errors;
76
+ }
77
+
78
+ /**
79
+ * Register WP's default assets and plugin installer assets
80
+ */
81
+ protected function enqueue()
82
+ {
83
+ wp_enqueue_style('common');
84
+ wp_enqueue_style('buttons');
85
+
86
+ /**
87
+ * GDPR installer custom styles
88
+ */
89
+ wp_enqueue_style(
90
+ 'gdpr-installer',
91
+ gdpr('config')->get('plugin.url') . 'assets/gdpr-installer.css'
92
+ );
93
+
94
+ wp_enqueue_style(
95
+ 'select2css',
96
+ '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.min.css'
97
+ );
98
+
99
+ wp_enqueue_script('jquery');
100
+ wp_enqueue_script('jquery-ui-core');
101
+ wp_enqueue_script('jquery-ui-widget');
102
+ wp_enqueue_script('jquery-ui-mouse');
103
+ wp_enqueue_script('jquery-ui-sortable');
104
+ wp_enqueue_script('jquery-ui-tabs');
105
+ wp_enqueue_script(
106
+ 'select2',
107
+ '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.min.js',
108
+ ['jquery']
109
+ );
110
+ wp_enqueue_script(
111
+ 'conditional-show',
112
+ gdpr('config')->get('plugin.url') . 'assets/conditional-show.js',
113
+ ['jquery']
114
+ );
115
+
116
+ //global $wp_scripts;
117
+ //$ui = $wp_scripts->query('jquery-ui-core');
118
+ //wp_enqueue_style('jquery-ui-smoothness', "//ajax.googleapis.com/ajax/libs/jqueryui/{$ui->ver}/themes/smoothness/jquery-ui.min.css", false, null);
119
+
120
+ wp_enqueue_script(
121
+ 'jquery-repeater',
122
+ gdpr('config')->get('plugin.url') . 'assets/jquery.repeater.min.js',
123
+ ['jquery']
124
+ );
125
+
126
+ /**
127
+ * Installer javascript
128
+ */
129
+ wp_enqueue_script(
130
+ 'gdpr-installer',
131
+ gdpr('config')->get('plugin.url') . 'assets/gdpr-installer.js',
132
+ ['jquery', 'select2']
133
+ );
134
+ }
135
+
136
+ /**
137
+ * Render the installer page header - html head, form, logo
138
+ */
139
+ protected function renderHeader()
140
+ {
141
+ echo gdpr('view')->render('installer/header', ['activeSteps' => $this->activeSteps]);
142
+ }
143
+
144
+ /**
145
+ * Render the installer page content - should be overridden by child class
146
+ */
147
+ protected function renderContent()
148
+ {
149
+ echo gdpr('view')->render($this->template);
150
+ }
151
+
152
+ /**
153
+ * Create and render the nonce based on the name of the current step
154
+ */
155
+ protected function renderNonce()
156
+ {
157
+ $nonce = wp_create_nonce($this->slug);
158
+ echo gdpr('view')->render('installer/nonce', compact('nonce'));
159
+ }
160
+
161
+ /**
162
+ * Render the footer - nav buttons and closing tags
163
+ */
164
+ protected function renderFooter()
165
+ {
166
+ echo gdpr('view')->render('installer/footer');
167
+ }
168
+
169
+ /**
170
+ * @return string
171
+ */
172
+ public function getUrl()
173
+ {
174
+ return gdpr('config')->get('installer.wizardUrl') . $this->slug;
175
+ }
176
+
177
+ /**
178
+ * @return string
179
+ */
180
+ public function getSlug()
181
+ {
182
+ if (is_null($this->slug)) {
183
+ trigger_error("GDPR: Slug not defined for step!", E_USER_ERROR);
184
+ }
185
+
186
+ return $this->slug;
187
+ }
188
+
189
+ /**
190
+ * @return string
191
+ */
192
+ public function getType()
193
+ {
194
+ if (is_null($this->type)) {
195
+ trigger_error("GDPR: Type not defined for step {$this->slug}", E_USER_ERROR);
196
+ }
197
+
198
+ return $this->type;
199
+ }
200
+ }
src/Installer/InstallerStepInterface.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Installer;
4
+
5
+ interface InstallerStepInterface
6
+ {
7
+ public function run();
8
+
9
+ public function validateNonce();
10
+
11
+ public function validate();
12
+
13
+ public function submit();
14
+
15
+ public function getErrors();
16
+
17
+ public function getUrl();
18
+
19
+ public function getSlug();
20
+
21
+ public function getType();
22
+ }
src/Installer/InstallerWizard.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Installer;
4
+
5
+ /**
6
+ * Handle the installer wizard pages
7
+ *
8
+ * Class InstallerWizard
9
+ *
10
+ * @package Codelight\GDPR\Installer
11
+ */
12
+ class InstallerWizard
13
+ {
14
+ /**
15
+ * InstallerWizard constructor.
16
+ */
17
+ public function __construct()
18
+ {
19
+ $this->configure();
20
+
21
+ add_action('admin_menu', [$this, 'registerWizardPage']);
22
+ }
23
+
24
+ /**
25
+ * Register the installer page with WordPress
26
+ */
27
+ public function registerWizardPage()
28
+ {
29
+ add_dashboard_page( '', '', 'manage_options', 'gdpr-setup', '' );
30
+ }
31
+
32
+ /**
33
+ * Set up the configuration object
34
+ */
35
+ protected function configure()
36
+ {
37
+ gdpr('config')->set('installer.wizardUrl', self_admin_url("index.php?page=gdpr-setup&gdpr-step="));
38
+ }
39
+
40
+ /**
41
+ * Check if we are already on the installer page
42
+ *
43
+ * @return bool
44
+ */
45
+ public function isWizardPage()
46
+ {
47
+ return isset($_GET['page']) && 'gdpr-setup' === $_GET['page'];
48
+ }
49
+ }
src/Installer/Steps/ConfigurationPages.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Installer\Steps;
4
+
5
+ use Codelight\GDPR\Installer\InstallerStep;
6
+ use Codelight\GDPR\Installer\InstallerStepInterface;
7
+
8
+ class ConfigurationPages extends InstallerStep implements InstallerStepInterface
9
+ {
10
+ protected $slug = 'configuration-pages';
11
+
12
+ protected $type = 'wizard';
13
+
14
+ protected $template = 'installer/steps/configuration-pages';
15
+
16
+ protected $activeSteps = 1;
17
+
18
+ protected function renderContent()
19
+ {
20
+ $privacyToolsPage = gdpr('options')->get('tools_page');
21
+ $privacyToolsPageSelector = wp_dropdown_pages([
22
+ 'name' => 'gdpr_tools_page',
23
+ 'show_option_none' => __('&mdash; Create a new page &mdash;'),
24
+ 'option_none_value' => 'new',
25
+ 'selected' => $privacyToolsPage ? $privacyToolsPage : 'new',
26
+ 'echo' => false,
27
+ 'class' => 'gdpr-select js-gdpr-select2',
28
+ ]);
29
+
30
+ echo gdpr('view')->render(
31
+ $this->template,
32
+ compact(
33
+ 'policyPage',
34
+ 'policyPageSelector',
35
+ 'privacyToolsPage',
36
+ 'privacyToolsPageSelector'
37
+ )
38
+ );
39
+ }
40
+
41
+ public function submit()
42
+ {
43
+ if (isset($_POST['gdpr_create_tools_page']) && 'yes' === $_POST['gdpr_create_tools_page']) {
44
+ $id = $this->createPrivacyToolsPage();
45
+ gdpr('options')->set('tools_page', $id);
46
+ } else {
47
+ gdpr('options')->set('tools_page', $_POST['gdpr_tools_page']);
48
+ }
49
+ }
50
+
51
+ protected function createPrivacyToolsPage()
52
+ {
53
+ $id = wp_insert_post([
54
+ 'post_content' => '[gdpr_privacy_tools]',
55
+ 'post_title' => __('Privacy Tools', 'gdpr'),
56
+ 'post_type' => 'page',
57
+ ]);
58
+
59
+ return $id;
60
+ }
61
+ }
src/Installer/Steps/ConfigurationSettings.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Installer\Steps;
4
+
5
+ use Codelight\GDPR\Installer\InstallerStep;
6
+ use Codelight\GDPR\Installer\InstallerStepInterface;
7
+
8
+ class ConfigurationSettings extends InstallerStep implements InstallerStepInterface
9
+ {
10
+ protected $slug = 'configuration-settings';
11
+
12
+ protected $type = 'wizard';
13
+
14
+ protected $template = 'installer/steps/configuration-settings';
15
+
16
+ protected $activeSteps = 1;
17
+
18
+ protected function renderContent()
19
+ {
20
+ $privacyToolsPageUrl = get_permalink(gdpr('options')->get('tools_page'));
21
+
22
+ $deleteAction = gdpr('options')->get('delete_action');
23
+ $deleteActionEmail = gdpr('options')->get('delete_action_email');
24
+
25
+ $exportAction = gdpr('options')->get('export_action');
26
+ $exportActionEmail = gdpr('options')->get('export_action_email');
27
+
28
+ $reassign = gdpr('options')->get('delete_action_reassign');
29
+ $reassignUser = gdpr('options')->get('delete_action_reassign_user');
30
+
31
+ echo gdpr('view')->render(
32
+ $this->template,
33
+ compact(
34
+ 'deleteAction',
35
+ 'deleteActionEmail',
36
+ 'exportAction',
37
+ 'exportActionEmail',
38
+ 'privacyToolsPageUrl',
39
+ 'reassign',
40
+ 'reassignUser'
41
+ )
42
+ );
43
+ }
44
+
45
+ public function submit()
46
+ {
47
+ if (isset($_POST['gdpr_export_action'])) {
48
+ gdpr('options')->set('delete_action', $_POST['gdpr_export_action']);
49
+ }
50
+
51
+ if (isset($_POST['gdpr_export_action_email'])) {
52
+ gdpr('options')->set('delete_action_email', $_POST['gdpr_export_action_email']);
53
+ }
54
+
55
+ if (isset($_POST['gdpr_delete_action'])) {
56
+ gdpr('options')->set('delete_action', $_POST['gdpr_delete_action']);
57
+ }
58
+
59
+ if (isset($_POST['gdpr_delete_action_email'])) {
60
+ gdpr('options')->set('delete_action_email', $_POST['gdpr_delete_action_email']);
61
+ }
62
+
63
+ if (isset($_POST['gdpr_delete_action_reassign'])) {
64
+ gdpr('options')->set('delete_action_reassign', $_POST['gdpr_delete_action_reassign']);
65
+ }
66
+
67
+ if (isset($_POST['gdpr_delete_action_reassign_user'])) {
68
+ gdpr('options')->set('delete_action_reassign_user', $_POST['gdpr_delete_action_reassign_user']);
69
+ }
70
+ }
71
+ }
src/Installer/Steps/Consent.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Installer\Steps;
4
+
5
+ use Codelight\GDPR\Installer\InstallerStep;
6
+ use Codelight\GDPR\Installer\InstallerStepInterface;
7
+
8
+ class Consent extends InstallerStep implements InstallerStepInterface
9
+ {
10
+ protected $slug = 'consent';
11
+
12
+ protected $type = 'wizard';
13
+
14
+ protected $template = 'installer/steps/consent';
15
+
16
+ protected $activeSteps = 3;
17
+
18
+ protected function renderContent()
19
+ {
20
+ $isRegistrationOpen = get_option('users_can_register');
21
+ $isCommentsEnabled = class_exists('Disable_Comments') ? false : true;
22
+ $privacyToolsPageUrl = get_permalink(gdpr('options')->get('tools_page'));
23
+ $hasGravityForms = class_exists('\GFForms');
24
+
25
+ echo gdpr('view')->render(
26
+ $this->template,
27
+ compact('isRegistrationOpen', 'isCommentsEnabled', 'privacyToolsPageUrl', 'hasGravityForms')
28
+ );
29
+ }
30
+ }
src/Installer/Steps/Disclaimer.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace Codelight\GDPR\Installer\Steps;
5
+
6
+
7
+ use Codelight\GDPR\Installer\InstallerStep;
8
+ use Codelight\GDPR\Installer\InstallerStepInterface;
9
+
10
+ class Disclaimer extends InstallerStep implements InstallerStepInterface
11
+ {
12
+ protected $slug = 'disclaimer';
13
+
14
+ protected $type = 'wizard';
15
+
16
+ protected $template = 'installer/steps/disclaimer';
17
+
18
+ protected $activeSteps = 0;
19
+
20
+ public function submit()
21
+ {
22
+ gdpr('options')->set('plugin_disclaimer_accepted', 'yes');
23
+ }
24
+ }
src/Installer/Steps/Finish.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Installer\Steps;
4
+
5
+ use Codelight\GDPR\Installer\InstallerStep;
6
+ use Codelight\GDPR\Installer\InstallerStepInterface;
7
+
8
+ class Finish extends InstallerStep implements InstallerStepInterface
9
+ {
10
+ protected $slug = 'finish';
11
+
12
+ protected $type = 'wizard';
13
+
14
+ protected $template = 'installer/steps/finish';
15
+
16
+ protected $activeSteps = 4;
17
+
18
+ public function submit()
19
+ {
20
+ gdpr('options')->set('is_installed', true);
21
+ }
22
+
23
+ protected function renderFooter()
24
+ {
25
+ echo gdpr('view')->render('installer/footer', ['disableBackButton' => true]);
26
+ }
27
+ }
src/Installer/Steps/Integrations.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Installer\Steps;
4
+
5
+ use Codelight\GDPR\Installer\InstallerStep;
6
+ use Codelight\GDPR\Installer\InstallerStepInterface;
7
+
8
+ class Integrations extends InstallerStep implements InstallerStepInterface
9
+ {
10
+ protected $slug = 'integrations';
11
+
12
+ protected $type = 'wizard';
13
+
14
+ protected $template = 'installer/steps/integrations';
15
+
16
+ protected $activeSteps = 4;
17
+
18
+ protected function renderContent()
19
+ {
20
+ $enableThemeCompatibility = gdpr('options')->get('enable_theme_compatibility');
21
+ $currentTheme = gdpr('themes')->getCurrentThemeName();
22
+ $isThemeSupported = gdpr('themes')->isCurrentThemeSupported();
23
+ $isThemeSupported = true;
24
+
25
+ $hasWooCommerce = false;
26
+ $hasEDD = false;
27
+
28
+ echo gdpr('view')->render(
29
+ $this->template,
30
+ compact(
31
+ 'enableThemeCompatibility',
32
+ 'hasEDD',
33
+ 'hasWooCommerce',
34
+ 'currentTheme',
35
+ 'isThemeSupported'
36
+ )
37
+ );
38
+ }
39
+
40
+ public function submit()
41
+ {
42
+ if (isset($_POST['gdpr_enable_theme_compatibility']) && 'yes' === $_POST['gdpr_enable_theme_compatibility']) {
43
+ gdpr('options')->set('enable_theme_compatibility', true);
44
+ } else {
45
+ gdpr('options')->set('enable_theme_compatibility', false);
46
+ }
47
+ }
48
+ }
src/Installer/Steps/PolicyContents.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ namespace Codelight\GDPR\Installer\Steps;
5
+
6
+
7
+ use Codelight\GDPR\Installer\InstallerStep;
8
+ use Codelight\GDPR\Installer\InstallerStepInterface;
9
+
10
+ class PolicyContents extends InstallerStep implements InstallerStepInterface
11
+ {
12
+ protected $slug = 'policy-contents';
13
+
14
+ protected $type = 'wizard';
15
+
16
+ protected $template = 'installer/steps/policy-contents';
17
+
18
+ protected $activeSteps = 2;
19
+
20
+ protected function renderContent()
21
+ {
22
+ $policyUrl = get_permalink(gdpr('options')->get('policy_page'));
23
+ $editPolicyUrl = get_edit_post_link(gdpr('options')->get('policy_page'));
24
+ $policyGenerated = gdpr('options')->get('policy_generated');
25
+
26
+ echo gdpr('view')->render(
27
+ $this->template,
28
+ compact('policyUrl', 'editPolicyUrl', 'policyGenerated')
29
+ );
30
+ }
31
+ }
src/Installer/Steps/PolicySettings.php ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Installer\Steps;
4
+
5
+ use Codelight\GDPR\Installer\InstallerStep;
6
+ use Codelight\GDPR\Installer\InstallerStepInterface;
7
+
8
+ class PolicySettings extends InstallerStep implements InstallerStepInterface
9
+ {
10
+ protected $slug = 'policy-settings';
11
+
12
+ protected $type = 'wizard';
13
+
14
+ protected $template = 'installer/steps/policy-settings';
15
+
16
+ protected $activeSteps = 2;
17
+
18
+ protected function renderContent()
19
+ {
20
+ $policyPage = gdpr('options')->get('policy_page');
21
+ $policyPageSelector = wp_dropdown_pages([
22
+ 'name' => 'gdpr_policy_page',
23
+ 'show_option_none' => __('&mdash; Create a new page &mdash;'),
24
+ 'option_none_value' => 'new',
25
+ 'selected' => $policyPage ? $policyPage : 'new',
26
+ 'echo' => false,
27
+ 'class' => 'gdpr-select js-gdpr-select2',
28
+ ]);
29
+
30
+ $hasTermsPage = gdpr('options')->get('has_terms_page');
31
+ $termsPage = gdpr('options')->get('terms_page');
32
+
33
+ // Woo compatibility
34
+ if (!$termsPage && get_option('woocommerce_terms_page_id')) {
35
+ $hasTermsPage = 'yes';
36
+ $termsPage = get_option('woocommerce_terms_page_id');
37
+ $termsPageNote = __(
38
+ 'We have automatically selected your WooCommerce Terms & Conditions page.',
39
+ 'gdpr-admin'
40
+ );
41
+ } else {
42
+ $termsPageNote = false;
43
+ }
44
+
45
+ $termsPageSelector = wp_dropdown_pages([
46
+ 'name' => 'gdpr_terms_page',
47
+ 'show_option_none' => __('&mdash; Create a new page &mdash;'),
48
+ 'option_none_value' => 'new',
49
+ 'selected' => $termsPage ? $termsPage : 'new',
50
+ 'echo' => false,
51
+ 'class' => 'gdpr-select js-gdpr-select2',
52
+ ]);
53
+
54
+ $companyName = gdpr('options')->get('company_name');
55
+ $companyLocation = gdpr('options')->get('company_location');
56
+ $countryOptions = gdpr('helpers')->getCountrySelectOptions($companyLocation);
57
+ $contactEmail = gdpr('options')->get('contact_email') ?
58
+ gdpr('options')->get('contact_email') :
59
+ get_option('admin_email');
60
+
61
+ $representativeContactName = gdpr('options')->get('representative_contact_name');
62
+ $representativeContactEmail = gdpr('options')->get('representative_contact_email');
63
+ $representativeContactPhone = gdpr('options')->get('representative_contact_phone');
64
+
65
+ $dpaWebsite = gdpr('options')->get('dpa_name');
66
+ $dpaEmail = gdpr('options')->get('dpa_email');
67
+ $dpaPhone = gdpr('options')->get('dpa_phone');
68
+ $dpaData = json_encode(gdpr('helpers')->getDataProtectionAuthorities());
69
+
70
+ $hasDPO = gdpr('options')->get('has_dpo');
71
+ $dpoName = gdpr('options')->get('dpo_name');
72
+ $dpoEmail = gdpr('options')->get('dpo_email');
73
+
74
+ echo gdpr('view')->render(
75
+ $this->template,
76
+ compact(
77
+ 'policyPage',
78
+ 'policyPageSelector',
79
+ 'companyName',
80
+ 'companyLocation',
81
+ 'contactEmail',
82
+ 'countryOptions',
83
+ 'hasDPO',
84
+ 'dpoEmail',
85
+ 'dpoName',
86
+ 'representativeContactName',
87
+ 'representativeContactEmail',
88
+ 'representativeContactPhone',
89
+ 'dpaWebsite',
90
+ 'dpaEmail',
91
+ 'dpaPhone',
92
+ 'dpaData',
93
+ 'hasTermsPage',
94
+ 'termsPage',
95
+ 'termsPageSelector',
96
+ 'termsPageNote'
97
+ )
98
+ );
99
+ }
100
+
101
+ /*
102
+ public function validate()
103
+ {
104
+ if (!is_email($_POST['gdpr_contact_email'])) {
105
+ $this->errors = 'Company email is not a valid email!';
106
+ return false;
107
+ }
108
+
109
+ return true;
110
+
111
+ //filter_var($url, FILTER_VALIDATE_URL) === FALSE
112
+ }
113
+ */
114
+
115
+ public function submit()
116
+ {
117
+ /**
118
+ * Policy page
119
+ */
120
+ if (isset($_POST['gdpr_create_policy_page']) && 'yes' === $_POST['gdpr_create_policy_page']) {
121
+ $id = $this->createPolicyPage();
122
+ gdpr('options')->set('policy_page', $id);
123
+ } else {
124
+ gdpr('options')->set('policy_page', $_POST['gdpr_policy_page']);
125
+ }
126
+
127
+ /**
128
+ * 'Generate policy' checkbox
129
+ */
130
+ if (isset($_POST['gdpr_generate_policy']) && 'yes' === $_POST['gdpr_generate_policy']) {
131
+ $this->generatePolicy();
132
+ gdpr('options')->set('policy_generated', true);
133
+ } else {
134
+ gdpr('options')->set('policy_generated', false);
135
+ }
136
+
137
+ /**
138
+ * Company information
139
+ */
140
+ gdpr('options')->set('company_name', $_POST['gdpr_company_name']);
141
+ gdpr('options')->set('company_location', $_POST['gdpr_company_location']);
142
+
143
+ if (is_email($_POST['gdpr_contact_email'])) {
144
+ gdpr('options')->set('contact_email', $_POST['gdpr_contact_email']);
145
+ }
146
+
147
+ /**
148
+ * Data Protection Officer
149
+ */
150
+ if (isset($_POST['gdpr_has_dpo'])) {
151
+ gdpr('options')->set('has_dpo', $_POST['gdpr_has_dpo']);
152
+ }
153
+
154
+ gdpr('options')->set('dpo_name', $_POST['gdpr_dpo_name']);
155
+
156
+ if (is_email($_POST['gdpr_dpo_email'])) {
157
+ gdpr('options')->set('dpo_email', $_POST['gdpr_dpo_email']);
158
+ }
159
+
160
+ /**
161
+ * Representative contact
162
+ */
163
+ gdpr('options')->set('representative_contact_name', $_POST['gdpr_representative_contact_name']);
164
+ gdpr('options')->set('representative_contact_phone', $_POST['gdpr_representative_contact_phone']);
165
+
166
+ if (is_email($_POST['gdpr_representative_contact_email'])) {
167
+ gdpr('options')->set('representative_contact_email', $_POST['gdpr_representative_contact_email']);
168
+ }
169
+
170
+ /**
171
+ * Data protection authority
172
+ */
173
+ gdpr('options')->set('dpa_website', $_POST['gdpr_dpa_website']);
174
+ gdpr('options')->set('dpa_phone', $_POST['gdpr_dpa_phone']);
175
+
176
+ if (is_email($_POST['gdpr_dpa_email'])) {
177
+ gdpr('options')->set('dpa_email', $_POST['gdpr_dpa_email']);
178
+ }
179
+
180
+
181
+ /**
182
+ * Terms page
183
+ */
184
+ if (isset($_POST['gdpr_has_terms_page'])) {
185
+ gdpr('options')->set('has_terms_page', $_POST['gdpr_has_terms_page']);
186
+ }
187
+
188
+ if (isset($_POST['gdpr_has_terms_page']) && 'yes' === $_POST['gdpr_has_terms_page'] && isset($_POST['gdpr_terms_page'])) {
189
+ gdpr('options')->set('terms_page', $_POST['gdpr_terms_page']);
190
+ } else {
191
+ gdpr('options')->delete('terms_page');
192
+ }
193
+ }
194
+
195
+ protected function createPolicyPage()
196
+ {
197
+ $id = wp_insert_post([
198
+ 'post_title' => __('Privacy Policy', 'gdpr'),
199
+ 'post_type' => 'page',
200
+ ]);
201
+
202
+ return $id;
203
+ }
204
+
205
+ protected function generatePolicy()
206
+ {
207
+ wp_update_post([
208
+ 'ID' => gdpr('options')->get('policy_page'),
209
+ 'post_content' => gdpr('view')->render(
210
+ 'policy/policy'
211
+ ),
212
+ ]);
213
+ }
214
+ }
src/Installer/Steps/Welcome.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Installer\Steps;
4
+
5
+ use Codelight\GDPR\Installer\InstallerStep;
6
+ use Codelight\GDPR\Installer\InstallerStepInterface;
7
+
8
+ /**
9
+ * Handle the first step on installer screen
10
+ *
11
+ * Class Welcome
12
+ *
13
+ * @package Codelight\GDPR\Installer\Steps
14
+ */
15
+ class Welcome extends InstallerStep implements InstallerStepInterface
16
+ {
17
+ protected $slug = 'welcome';
18
+
19
+ protected $type = 'wizard';
20
+
21
+ protected $template = 'installer/steps/welcome';
22
+
23
+ protected $activeSteps = 0;
24
+
25
+ protected function renderFooter()
26
+ {
27
+ echo gdpr('view')->render('installer/footer', ['disableBackButton' => true]);
28
+ }
29
+ }
src/Modules/ContactForm7/ContactForm7.php ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Modules\ContactForm7;
4
+
5
+ use Codelight\GDPR\Components\Consent\ConsentManager;
6
+ use Codelight\GDPR\DataSubject\DataSubjectManager;
7
+
8
+ class ContactForm7
9
+ {
10
+ public function __construct(DataSubjectManager $dataSubjectManager, ConsentManager $consentManager)
11
+ {
12
+ $this->dataSubjectManager = $dataSubjectManager;
13
+ $this->consentManager = $consentManager;
14
+
15
+ //add_action('wpcf7_init', [$this, 'addFormTags'], 10, 3);
16
+ //add_action('wpcf7_admin_init', [$this, 'addFormTagGenerators'], 9999, 3);
17
+
18
+ add_action('wpcf7_before_send_mail', [$this, 'processFormSubmission'], 10, 3);
19
+ }
20
+
21
+ public function addFormTags()
22
+ {
23
+ wpcf7_add_form_tag(
24
+ ['gdpr_consent_text'],
25
+ [$this, 'renderPrivacyTag']
26
+ );
27
+ }
28
+
29
+ public function addFormTagGenerators()
30
+ {
31
+ $generator = \WPCF7_TagGenerator::get_instance();
32
+
33
+ $generator->add(
34
+ 'gdpr_privacy',
35
+ __('gdpr terms txt', 'gdpr-admin'),
36
+ [$this, 'generatePrivacyTag']
37
+ );
38
+ }
39
+
40
+ public function generatePrivacyTag($contactForm, $args)
41
+ {
42
+ $args = wp_parse_args( $args, array() );
43
+ $description = __( "Generate the default text for your Privacy Policy acceptance checkbox. For more details, see %s.", 'gdpr-admin' );
44
+ $descLink = wpcf7_link( __( 'https://TODO', 'gdpr-admin' ), __( 'GDPR Terms', 'gdpr-admin' ) );
45
+ $content = $this->renderPrivacyTag();
46
+
47
+ echo gdpr('view')->render(
48
+ 'modules/contact-form-7/generator-privacy',
49
+ compact('description', 'descLink', 'args', 'content')
50
+ );
51
+ }
52
+
53
+ public function renderPrivacyTag()
54
+ {
55
+ $privacyPolicyUrl = get_permalink(gdpr('options')->get('policy_page'));
56
+
57
+ return gdpr('view')->render(
58
+ 'modules/contact-form-7/content-privacy',
59
+ compact('privacyPolicyUrl')
60
+ );
61
+ }
62
+
63
+ public function processFormSubmission(\WPCF7_ContactForm $form, $abort, \WPCF7_Submission $submission)
64
+ {
65
+ $consents = $this->findConsents($form, $submission);
66
+
67
+ if (!count($consents)) {
68
+ return;
69
+ }
70
+
71
+ $email = $this->findEmail($submission);
72
+
73
+ if (!$email) {
74
+ return;
75
+ }
76
+
77
+ $dataSubject = $this->dataSubjectManager->getByEmail($email);
78
+
79
+ foreach ($consents as $consent) {
80
+ $dataSubject->giveConsent($consent);
81
+ }
82
+ }
83
+
84
+ public function findConsents(\WPCF7_ContactForm $form, \WPCF7_Submission $submission)
85
+ {
86
+ $consents = [];
87
+
88
+ foreach ($form->scan_form_tags() as $tag) {
89
+ /* @var $tag \WPCF7_FormTag */
90
+ if ('acceptance' === $tag->type && $submission->get_posted_data()[$tag->name] && $this->consentManager->isRegisteredConsent($tag->name)) {
91
+ $consents[] = $tag->name;
92
+ }
93
+ }
94
+
95
+ return $consents;
96
+ }
97
+
98
+ public function findEmail(\WPCF7_Submission $submission)
99
+ {
100
+ if (isset($submission->get_posted_data()['your-email'])) {
101
+ return $submission->get_posted_data()['your-email'];
102
+ }
103
+
104
+ if (isset($submission->get_posted_data()['email'])) {
105
+ return $submission->get_posted_data()['email'];
106
+ }
107
+ }
108
+ }
src/Modules/WPML/WPML.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Modules\WPML;
4
+
5
+ class WPML
6
+ {
7
+ protected $prefix = 'gdpr_';
8
+
9
+ protected $translatableOptions = [
10
+ 'tools_page',
11
+ 'policy_page',
12
+ 'terms_page',
13
+ 'consent_info',
14
+ ];
15
+
16
+ public function __construct()
17
+ {
18
+ if (!class_exists('Sitepress')) {
19
+ return;
20
+ }
21
+
22
+ $this->setupOptionFilters();
23
+ }
24
+
25
+ protected function setupOptionFilters()
26
+ {
27
+ foreach ($this->translatableOptions as $option) {
28
+ add_filter("gdpr/options/get/{$option}", [$this, 'getTranslatedOption']);
29
+
30
+ $option = $this->prefix($option);
31
+ add_filter("pre_update_option_{$option}", [$this, 'setTranslatedOption'], 10, 2);
32
+ }
33
+
34
+ add_filter('gdpr/options/get/consent_types', [$this, 'getConsentTypes']);
35
+ add_filter('gdpr/options/set/consent_types', [$this, 'saveConsentTypes']);
36
+ }
37
+
38
+ public function getTranslatedOption($option)
39
+ {
40
+ if (!defined('ICL_LANGUAGE_CODE')) {
41
+ if (is_array($option)) {
42
+ return '';
43
+ } else {
44
+ return $option;
45
+ }
46
+ }
47
+
48
+ if (isset($option[ICL_LANGUAGE_CODE])) {
49
+ return $option[ICL_LANGUAGE_CODE];
50
+ } else {
51
+ return '';
52
+ }
53
+ }
54
+
55
+ public function setTranslatedOption($newValue, $oldValue)
56
+ {
57
+ if (!defined('ICL_LANGUAGE_CODE')) {
58
+ return $newValue;
59
+ }
60
+
61
+ if (is_array($oldValue)) {
62
+ $value = $oldValue;
63
+ } else {
64
+ $value = [];
65
+ }
66
+
67
+ $value[ICL_LANGUAGE_CODE] = $newValue;
68
+
69
+ return $value;
70
+ }
71
+
72
+ public function getConsentTypes($consentTypes)
73
+ {
74
+ if (!defined('ICL_LANGUAGE_CODE')) {
75
+ return $consentTypes;
76
+ }
77
+
78
+ $code = ICL_LANGUAGE_CODE;
79
+ $filteredConsentTypes = [];
80
+
81
+ foreach ($consentTypes as $consentType) {
82
+
83
+ if ('privacy-policy' === $consentType['slug'] or 'terms-condition' === $consentType['slug']) {
84
+ $filteredConsentTypes[] = [
85
+ 'slug' => isset($consentType['slug']) ? $consentType['slug'] : '',
86
+ 'visible' => isset($consentType['visible']) ? $consentType['visible'] : 0,
87
+ 'title' => isset($consentType["title"]) ? $consentType["title"] : '',
88
+ 'description' => isset($consentType["description"]) ? $consentType["description"] : '',
89
+ ];
90
+ } else {
91
+ $filteredConsentTypes[] = [
92
+ 'slug' => isset($consentType['slug']) ? $consentType['slug'] : '',
93
+ 'visible' => isset($consentType['visible']) ? $consentType['visible'] : 0,
94
+ 'title' => isset($consentType["{$code}_title"]) ? $consentType["{$code}_title"] : '',
95
+ 'description' => isset($consentType["{$code}_description"]) ? $consentType["{$code}_description"] : '',
96
+ ];
97
+ }
98
+ }
99
+
100
+ return $filteredConsentTypes;
101
+ }
102
+
103
+ public function saveConsentTypes($newConsentTypes)
104
+ {
105
+ if (!defined('ICL_LANGUAGE_CODE')) {
106
+ return $consentTypes;
107
+ }
108
+
109
+ $code = ICL_LANGUAGE_CODE;
110
+ $translatedConsentTypes = [];
111
+ $currentConsentTypes = gdpr('options')->get('consent_types', null, false);
112
+
113
+ foreach ($newConsentTypes as &$newConsentType) {
114
+
115
+ // Match an existing consent type with the new one
116
+ $slug = $newConsentType['slug'];
117
+ $existingConsentType = current(array_filter($currentConsentTypes, function($value) use ($slug) {
118
+ return $value['slug'] === $slug;
119
+ }));
120
+
121
+ if ($existingConsentType) {
122
+ // We have a matching existing consent - update translations, keep the rest
123
+ $existingConsentType["{$code}_title"] = $newConsentType['title'];
124
+ $existingConsentType["{$code}_description"] = $newConsentType['description'];
125
+
126
+ $translatedConsentTypes[] = $existingConsentType;
127
+ } else {
128
+ // We do not have a matching existin consent - just adjust the keys for language
129
+ $newConsentType["{$code}_title"] = $newConsentType['title'];
130
+ $newConsentType["{$code}_description"] = $newConsentType['description'];
131
+ unset($newConsentType['title']);
132
+ unset($newConsentType['description']);
133
+
134
+ $translatedConsentTypes[] = $newConsentType;
135
+ }
136
+ }
137
+
138
+ return $translatedConsentTypes;
139
+ }
140
+
141
+ public function prefix($name)
142
+ {
143
+ if (0 === strpos($name, $this->prefix)) {
144
+ return $name;
145
+ }
146
+
147
+ return $this->prefix . $name;
148
+ }
149
+ }
src/Options/Options.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Options;
4
+
5
+ /**
6
+ * Main class for handling options on a non-multisite install.
7
+ * Adapted from https://carlalexander.ca/designing-classes-wordpress-options-api/
8
+ *
9
+ * Class Options
10
+ *
11
+ * @package Codelight\GDPR\Options
12
+ */
13
+ class Options extends OptionsBase
14
+ {
15
+ /**
16
+ * Gets the option for the given name. Returns the default value if the value does not exist.
17
+ *
18
+ * @param string $name
19
+ * @param mixed $default
20
+ *
21
+ * @return mixed
22
+ */
23
+ public function get($name, $default = null, $enableFilter = true)
24
+ {
25
+ $value = get_option($this->prefix($name), $default);
26
+
27
+ if ($enableFilter) {
28
+ $value = apply_filters("gdpr/options/get/{$name}", $value);
29
+ }
30
+
31
+ if (is_array($default) && !is_array($value)) {
32
+ $value = (array) $value;
33
+ }
34
+
35
+ return $value;
36
+ }
37
+
38
+ /**
39
+ * Sets an option. Overwrites the existing option if the name is already in use.
40
+ *
41
+ * @param string $name
42
+ * @param mixed $value
43
+ */
44
+ public function set($name, $value, $enableFilter = true)
45
+ {
46
+ if ($enableFilter) {
47
+ $value = apply_filters("gdpr/options/set/{$name}", $value, get_option($this->prefix($name)));
48
+ }
49
+
50
+ update_option(
51
+ $this->prefix($name),
52
+ $value,
53
+ false
54
+ );
55
+ }
56
+
57
+ /**
58
+ * Removes the option with the given name.
59
+ *
60
+ * @param string $name
61
+ */
62
+ public function delete($name)
63
+ {
64
+ delete_option($this->prefix($name));
65
+ }
66
+ }
src/Options/OptionsBase.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR\Options;
4
+
5
+ /**
6
+ * Parent class for options.
7
+ * Adapted from https://carlalexander.ca/designing-classes-wordpress-options-api/
8
+ *
9
+ * Class OptionsBase
10
+ *
11
+ * @package Codelight\GDPR\Options
12
+ */
13
+ abstract class OptionsBase
14
+ {
15
+ /* @var string */
16
+ protected $prefix = 'gdpr_';
17
+
18
+ /**
19
+ * Auto-prefix all options
20
+ *
21
+ * @param $name
22
+ * @return string
23
+ */
24
+ public function prefix($name)
25
+ {
26
+ // Check for accidental duplicate prefix
27
+ if (0 === strpos($name, $this->prefix)) {
28
+ trigger_error("You appear to have a duplicate prefix for option {$name}", E_USER_NOTICE);
29
+ return $name;
30
+ }
31
+
32
+ return $this->prefix . $name;
33
+ }
34
+
35
+ /**
36
+ * Checks if the option with the given name exists or not.
37
+ *
38
+ * @param string $name
39
+ *
40
+ * @return bool
41
+ */
42
+ public function has($name)
43
+ {
44
+ return null !== $this->get($name);
45
+ }
46
+
47
+ /**
48
+ * Gets the option for the given name. Returns the default value if the value does not exist.
49
+ *
50
+ * @param string $name
51
+ * @param mixed $default
52
+ *
53
+ * @return mixed
54
+ */
55
+ abstract public function get($name, $default = null);
56
+
57
+ /**
58
+ * Removes the option with the given name.
59
+ *
60
+ * @param string $name
61
+ */
62
+ abstract public function delete($name);
63
+
64
+ /**
65
+ * Sets an option. Overwrites the existing option if the name is already in use.
66
+ *
67
+ * @param string $name
68
+ * @param mixed $value
69
+ */
70
+ abstract public function set($name, $value);
71
+ }
src/Router.php ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR;
4
+
5
+ use Codelight\GDPR\DataSubject\DataSubjectAuthenticator;
6
+
7
+ /**
8
+ * Handles automatically identifying context and triggering actions based on $_REQUEST['gdpr_action']
9
+ *
10
+ * Class Router
11
+ *
12
+ * @package Codelight\GDPR
13
+ */
14
+ class Router
15
+ {
16
+ /* @var DataSubjectAuthenticator $authenticator */
17
+ protected $authenticator;
18
+
19
+ /**
20
+ * Router constructor.
21
+ *
22
+ * @param DataSubjectAuthenticator $authenticator
23
+ */
24
+ public function __construct(DataSubjectAuthenticator $authenticator)
25
+ {
26
+ $this->authenticator = $authenticator;
27
+
28
+ // Routing happens at priority 20 to allow other 'init' actions to complete before
29
+ add_action('init', [$this, 'routeFrontendRequest'], 20);
30
+ add_action('admin_init', [$this, 'routeAdminRequest'], 20);
31
+ }
32
+
33
+ /**
34
+ * Get and sanitize the action parameter
35
+ *
36
+ * @return bool|mixed
37
+ */
38
+ protected function getAction()
39
+ {
40
+ if (!isset($_REQUEST['gdpr_action'])) {
41
+ return false;
42
+ }
43
+
44
+ // Simple sanitization: allowed chars are alphanumeric, dash, underscore and forward slash.
45
+ return preg_replace("/[^a-zA-Z0-9_\-\/]/", "", $_REQUEST['gdpr_action']);
46
+ }
47
+
48
+ /**
49
+ * Detect and trigger proper action in front-end
50
+ *
51
+ * @param $action
52
+ */
53
+ public function routeFrontendRequest()
54
+ {
55
+ // Since the 'init' hooks runs in both admin and non-admin requests, double-check where we are
56
+ if (is_admin()) {
57
+ return;
58
+ }
59
+
60
+ // Handle identification by email
61
+ $this->authenticator->identify();
62
+
63
+ $action = $this->getAction();
64
+ $nonce = isset($_REQUEST['gdpr_nonce']) ? $_REQUEST['gdpr_nonce'] : null;
65
+
66
+ if (!$action) {
67
+ return;
68
+ }
69
+
70
+ $dataSubject = $this->authenticator->authenticate();
71
+
72
+ if ($dataSubject) {
73
+ $tag = "gdpr/frontend/privacy-tools-page/action/{$action}";
74
+ if (wp_verify_nonce($nonce, $tag)) {
75
+ $key = isset($_REQUEST['gdpr_key']) ? $_REQUEST['gdpr_key'] : null;
76
+ do_action($tag, $dataSubject, $key);
77
+ } else {
78
+ wp_die(
79
+ sprintf(
80
+ __('Nonce error for action "%s". Please go back and try again!', 'gdpr'),
81
+ esc_html($action)
82
+ )
83
+ );
84
+ }
85
+ } else {
86
+ $tag = "gdpr/frontend/action/{$action}";
87
+ if (wp_verify_nonce($nonce, $tag)) {
88
+ do_action($tag);
89
+ } else {
90
+ wp_die(
91
+ sprintf(
92
+ __('Nonce error for action "%s". Please go back and try again!', 'gdpr'),
93
+ esc_html($action)
94
+ )
95
+ );
96
+ }
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Detect and trigger proper action in admin
102
+ *
103
+ * @param $action
104
+ */
105
+ public function routeAdminRequest()
106
+ {
107
+ $action = $this->getAction();
108
+ $nonce = isset($_REQUEST['gdpr_nonce']) ? $_REQUEST['gdpr_nonce'] : null;
109
+
110
+ if (!$action) {
111
+ return;
112
+ }
113
+
114
+ if (isset($_GET['page']) && 'gdpr-profile' === $_GET['page']) {
115
+
116
+ $dataSubject = $this->authenticator->authenticate();
117
+ if ($dataSubject) {
118
+ $tag = "gdpr/dashboard/privacy-tools/action/{$action}";
119
+
120
+ if (wp_verify_nonce($nonce, $tag)) {
121
+ do_action($tag, $dataSubject);
122
+ } else {
123
+ wp_die(
124
+ sprintf(
125
+ __('Nonce error for action "%s". Please go back and try again!', 'gdpr'),
126
+ esc_html($action)
127
+ )
128
+ );
129
+ }
130
+ }
131
+ } else {
132
+ if ($this->checkAdminPermissions()) {
133
+
134
+ $tag = "gdpr/admin/action/{$action}";
135
+
136
+ if (wp_verify_nonce($nonce, $tag)) {
137
+ do_action($tag);
138
+ } else {
139
+ wp_die(
140
+ sprintf(
141
+ __('Nonce error for action "%s". Please go back and try again!', 'gdpr'),
142
+ esc_html($action)
143
+ )
144
+ );
145
+ }
146
+ } else {
147
+ wp_die(
148
+ sprintf(
149
+ __('You do not have the required permissions to perform this action!', 'gdpr-admin'),
150
+ esc_html($action)
151
+ )
152
+ );
153
+ }
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Check if the current user has the correct capability to perform an admin action
159
+ *
160
+ * @return bool
161
+ */
162
+ protected function checkAdminPermissions()
163
+ {
164
+ return current_user_can(apply_filters('gdpr/capability', 'manage_options'));
165
+ }
166
+ }
src/Setup.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR;
4
+
5
+ use Codelight\GDPR\Admin\AdminTab;
6
+ use Codelight\GDPR\Components\Consent\ConsentManager;
7
+ use Codelight\GDPR\Components\PrivacyToolsPage\PrivacyToolsPage;
8
+ use Codelight\GDPR\Components\PrivacyPolicy\PrivacyPolicy;
9
+ use Codelight\GDPR\Components\Support\Support;
10
+ use Codelight\GDPR\Components\WordpressComments\WordpressComments;
11
+ use Codelight\GDPR\DataSubject\DataExporter;
12
+ use Codelight\GDPR\DataSubject\DataSubjectAdmin;
13
+ use Codelight\GDPR\DataSubject\DataSubjectIdentificator;
14
+ use Codelight\GDPR\DataSubject\DataSubjectManager;
15
+ use Codelight\GDPR\Modules\ContactForm7\ContactForm7;
16
+ use Codelight\GDPR\Components\Themes\Themes;
17
+ use Codelight\GDPR\Components\WordpressUser\WordpressUser;
18
+ use Codelight\GDPR\Modules\WPML\WPML;
19
+ use Codelight\GDPR\Options\Options;
20
+
21
+ /**
22
+ * Instantiate components
23
+ *
24
+ * Class Setup
25
+ * @package Codelight\GDPR
26
+ */
27
+ class Setup
28
+ {
29
+ /**
30
+ * Setup constructor.
31
+ */
32
+ public function __construct()
33
+ {
34
+ $this->registerComponents();
35
+ $this->runComponents();
36
+
37
+ add_action('init', function() {
38
+
39
+ if (!is_admin()) {
40
+ return;
41
+ }
42
+
43
+ gdpr()->singleton(SetupAdmin::class);
44
+ gdpr(SetupAdmin::class);
45
+ }, 0);
46
+ }
47
+
48
+ /**
49
+ * Register required components in the container
50
+ */
51
+ protected function registerComponents()
52
+ {
53
+ gdpr()->bind(Router::class);
54
+ gdpr()->bind(DataExporter::class);
55
+
56
+ gdpr()->singleton(PrivacyToolsPage::class);
57
+
58
+ gdpr()->singleton(AdminTab::class);
59
+ gdpr()->singleton(DataSubjectManager::class);
60
+ gdpr()->singleton(DataSubjectIdentificator::class);
61
+ gdpr()->singleton(View::class);
62
+ gdpr()->singleton(Options::class);
63
+ gdpr()->singleton(ConsentManager::class);
64
+ gdpr()->singleton(Helpers::class);
65
+ gdpr()->singleton(Themes::class);
66
+
67
+ gdpr()->alias(View::class, 'view');
68
+ gdpr()->alias(Options::class, 'options');
69
+ gdpr()->alias(ConsentManager::class, 'consent');
70
+ gdpr()->alias(Helpers::class, 'helpers');
71
+ gdpr()->alias(Themes::class, 'themes');
72
+ }
73
+
74
+ /**
75
+ * Check which components should be ran and run them
76
+ */
77
+ protected function runComponents()
78
+ {
79
+ gdpr()->make(WPML::class);
80
+ gdpr()->make(Router::class);
81
+ gdpr()->make(DataSubjectIdentificator::class);
82
+ gdpr()->make(DataSubjectAdmin::class);
83
+ gdpr()->make(PrivacyToolsPage::class);
84
+ gdpr()->make(PrivacyPolicy::class);
85
+ gdpr()->make(WordpressComments::class);
86
+ gdpr()->make(WordpressUser::class);
87
+ gdpr()->make(Support::class);
88
+
89
+ // Integrations
90
+ gdpr()->make(Themes::class);
91
+ gdpr()->make(ContactForm7::class);
92
+ }
93
+ }
src/SetupAdmin.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR;
4
+
5
+ use Codelight\GDPR\Admin\AdminError;
6
+ use Codelight\GDPR\Admin\AdminNotice;
7
+ use Codelight\GDPR\Admin\Modal;
8
+ use Codelight\GDPR\Admin\WordpressAdmin;
9
+ use Codelight\GDPR\Admin\WordpressAdminPage;
10
+ use Codelight\GDPR\Components\Consent\ConsentAdmin;
11
+ use Codelight\GDPR\Installer\Installer;
12
+ use Codelight\GDPR\Installer\AdminInstallerNotice;
13
+
14
+ /**
15
+ * Register and set up admin components.
16
+ * This class is instantiated at admin_init priority 0
17
+ *
18
+ * Class SetupAdmin
19
+ *
20
+ * @package Codelight\GDPR
21
+ */
22
+ class SetupAdmin
23
+ {
24
+ /**
25
+ * SetupAdmin constructor.
26
+ */
27
+ public function __construct()
28
+ {
29
+ $this->registerComponents();
30
+ $this->runComponents();
31
+ }
32
+
33
+ /**
34
+ * Register components in the container
35
+ */
36
+ protected function registerComponents()
37
+ {
38
+ gdpr()->singleton(WordpressAdmin::class);
39
+ gdpr()->singleton(WordpressAdminPage::class);
40
+ gdpr()->singleton(Installer::class);
41
+
42
+ // Not a singleton.
43
+ gdpr()->alias(AdminNotice::class, 'admin-notice');
44
+ gdpr()->alias(AdminError::class, 'admin-error');
45
+ gdpr()->alias(AdminInstallerNotice::class, 'installer-notice');
46
+ gdpr()->alias(Modal::class, 'admin-modal');
47
+ gdpr()->alias(WordpressAdminPage::class, 'admin-page');
48
+
49
+ gdpr()->bind(ConsentAdmin::class);
50
+ }
51
+
52
+ /**
53
+ * Run components
54
+ */
55
+ protected function runComponents()
56
+ {
57
+ gdpr(WordpressAdmin::class);
58
+ gdpr(WordpressAdminPage::class);
59
+ gdpr(Installer::class);
60
+ gdpr(ConsentAdmin::class);
61
+ }
62
+ }
src/View.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Codelight\GDPR;
4
+
5
+ /**
6
+ * Handles locating templates from either the theme or plugin,
7
+ * injecting and extracting data and rendering them.
8
+ *
9
+ * Class View
10
+ * @package Codelight\GDPR
11
+ */
12
+ class View
13
+ {
14
+ /**
15
+ * View constructor.
16
+ */
17
+ public function __construct()
18
+ {
19
+ $this->dirs = $this->getTemplateDirectories();
20
+ }
21
+
22
+ /**
23
+ * Render a given template.
24
+ *
25
+ * @param $template
26
+ * @param array $data
27
+ * @param null $templateDir
28
+ *
29
+ * @return string
30
+ */
31
+ public function render($template, $data = [], $templateDir = null)
32
+ {
33
+ if (is_null($templateDir)) {
34
+ foreach ($this->dirs as $dir) {
35
+ if (file_exists($dir . $template . '.php')) {
36
+ $templateDir = $dir;
37
+ break;
38
+ }
39
+ }
40
+ }
41
+
42
+ extract($data);
43
+ ob_start();
44
+ require $templateDir . $template . '.php';
45
+
46
+ return ob_get_clean();
47
+ }
48
+
49
+ /**
50
+ * Get valid template directories and pass them through a filter
51
+ *
52
+ * @return array
53
+ */
54
+ protected function getTemplateDirectories()
55
+ {
56
+ $directories = array_filter([
57
+ get_stylesheet_directory() . '/gdpr-framework/',
58
+ get_template_directory() . '/gdpr-framework/',
59
+ gdpr('config')->get('plugin.template_path'),
60
+ ], 'is_dir');
61
+
62
+ return array_unique(apply_filters('gdpr/views', $directories));
63
+ }
64
+ }
vendor/autoload.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload.php @generated by Composer
4
+
5
+ require_once __DIR__ . '/composer/autoload_real.php';
6
+
7
+ return ComposerAutoloaderInit96f7d562f9c6dd12a2fa032fa886e6b0::getLoader();
vendor/composer/ClassLoader.php ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer\Autoload;
14
+
15
+ /**
16
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17
+ *
18
+ * $loader = new \Composer\Autoload\ClassLoader();
19
+ *
20
+ * // register classes with namespaces
21
+ * $loader->add('Symfony\Component', __DIR__.'/component');
22
+ * $loader->add('Symfony', __DIR__.'/framework');
23
+ *
24
+ * // activate the autoloader
25
+ * $loader->register();
26
+ *
27
+ * // to enable searching the include path (eg. for PEAR packages)
28
+ * $loader->setUseIncludePath(true);
29
+ *
30
+ * In this example, if you try to use a class in the Symfony\Component
31
+ * namespace or one of its children (Symfony\Component\Console for instance),
32
+ * the autoloader will first look for the class under the component/
33
+ * directory, and it will then fallback to the framework/ directory if not
34
+ * found before giving up.
35
+ *
36
+ * This class is loosely based on the Symfony UniversalClassLoader.
37
+ *
38
+ * @author Fabien Potencier <fabien@symfony.com>
39
+ * @author Jordi Boggiano <j.boggiano@seld.be>
40
+ * @see http://www.php-fig.org/psr/psr-0/
41
+ * @see http://www.php-fig.org/psr/psr-4/
42
+ */
43
+ class ClassLoader
44
+ {
45
+ // PSR-4
46
+ private $prefixLengthsPsr4 = array();
47
+ private $prefixDirsPsr4 = array();
48
+ private $fallbackDirsPsr4 = array();
49
+
50
+ // PSR-0
51
+ private $prefixesPsr0 = array();
52
+ private $fallbackDirsPsr0 = array();
53
+
54
+ private $useIncludePath = false;
55
+ private $classMap = array();
56
+ private $classMapAuthoritative = false;
57
+ private $missingClasses = array();
58
+ private $apcuPrefix;
59
+
60
+ public function getPrefixes()
61
+ {
62
+ if (!empty($this->prefixesPsr0)) {
63
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
64
+ }
65
+
66
+ return array();
67
+ }
68
+
69
+ public function getPrefixesPsr4()
70
+ {
71
+ return $this->prefixDirsPsr4;
72
+ }
73
+
74
+ public function getFallbackDirs()
75
+ {
76
+ return $this->fallbackDirsPsr0;
77
+ }
78
+
79
+ public function getFallbackDirsPsr4()
80
+ {
81
+ return $this->fallbackDirsPsr4;
82
+ }
83
+
84
+ public function getClassMap()
85
+ {
86
+ return $this->classMap;
87
+ }
88
+
89
+ /**
90
+ * @param array $classMap Class to filename map
91
+ */
92
+ public function addClassMap(array $classMap)
93
+ {
94
+ if ($this->classMap) {
95
+ $this->classMap = array_merge($this->classMap, $classMap);
96
+ } else {
97
+ $this->classMap = $classMap;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Registers a set of PSR-0 directories for a given prefix, either
103
+ * appending or prepending to the ones previously set for this prefix.
104
+ *
105
+ * @param string $prefix The prefix
106
+ * @param array|string $paths The PSR-0 root directories
107
+ * @param bool $prepend Whether to prepend the directories
108
+ */
109
+ public function add($prefix, $paths, $prepend = false)
110
+ {
111
+ if (!$prefix) {
112
+ if ($prepend) {
113
+ $this->fallbackDirsPsr0 = array_merge(
114
+ (array) $paths,
115
+ $this->fallbackDirsPsr0
116
+ );
117
+ } else {
118
+ $this->fallbackDirsPsr0 = array_merge(
119
+ $this->fallbackDirsPsr0,
120
+ (array) $paths
121
+ );
122
+ }
123
+
124
+ return;
125
+ }
126
+
127
+ $first = $prefix[0];
128
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
129
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
130
+
131
+ return;
132
+ }
133
+ if ($prepend) {
134
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
135
+ (array) $paths,
136
+ $this->prefixesPsr0[$first][$prefix]
137
+ );
138
+ } else {
139
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
140
+ $this->prefixesPsr0[$first][$prefix],
141
+ (array) $paths
142
+ );
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Registers a set of PSR-4 directories for a given namespace, either
148
+ * appending or prepending to the ones previously set for this namespace.
149
+ *
150
+ * @param string $prefix The prefix/namespace, with trailing '\\'
151
+ * @param array|string $paths The PSR-4 base directories
152
+ * @param bool $prepend Whether to prepend the directories
153
+ *
154
+ * @throws \InvalidArgumentException
155
+ */
156
+ public function addPsr4($prefix, $paths, $prepend = false)
157
+ {
158
+ if (!$prefix) {
159
+ // Register directories for the root namespace.
160
+ if ($prepend) {
161
+ $this->fallbackDirsPsr4 = array_merge(
162
+ (array) $paths,
163
+ $this->fallbackDirsPsr4
164
+ );
165
+ } else {
166
+ $this->fallbackDirsPsr4 = array_merge(
167
+ $this->fallbackDirsPsr4,
168
+ (array) $paths
169
+ );
170
+ }
171
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
172
+ // Register directories for a new namespace.
173
+ $length = strlen($prefix);
174
+ if ('\\' !== $prefix[$length - 1]) {
175
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
176
+ }
177
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
178
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
179
+ } elseif ($prepend) {
180
+ // Prepend directories for an already registered namespace.
181
+ $this->prefixDirsPsr4[$prefix] = array_merge(
182
+ (array) $paths,
183
+ $this->prefixDirsPsr4[$prefix]
184
+ );
185
+ } else {
186
+ // Append directories for an already registered namespace.
187
+ $this->prefixDirsPsr4[$prefix] = array_merge(
188
+ $this->prefixDirsPsr4[$prefix],
189
+ (array) $paths
190
+ );
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Registers a set of PSR-0 directories for a given prefix,
196
+ * replacing any others previously set for this prefix.
197
+ *
198
+ * @param string $prefix The prefix
199
+ * @param array|string $paths The PSR-0 base directories
200
+ */
201
+ public function set($prefix, $paths)
202
+ {
203
+ if (!$prefix) {
204
+ $this->fallbackDirsPsr0 = (array) $paths;
205
+ } else {
206
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Registers a set of PSR-4 directories for a given namespace,
212
+ * replacing any others previously set for this namespace.
213
+ *
214
+ * @param string $prefix The prefix/namespace, with trailing '\\'
215
+ * @param array|string $paths The PSR-4 base directories
216
+ *
217
+ * @throws \InvalidArgumentException
218
+ */
219
+ public function setPsr4($prefix, $paths)
220
+ {
221
+ if (!$prefix) {
222
+ $this->fallbackDirsPsr4 = (array) $paths;
223
+ } else {
224
+ $length = strlen($prefix);
225
+ if ('\\' !== $prefix[$length - 1]) {
226
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
227
+ }
228
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
229
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Turns on searching the include path for class files.
235
+ *
236
+ * @param bool $useIncludePath
237
+ */
238
+ public function setUseIncludePath($useIncludePath)
239
+ {
240
+ $this->useIncludePath = $useIncludePath;
241
+ }
242
+
243
+ /**
244
+ * Can be used to check if the autoloader uses the include path to check
245
+ * for classes.
246
+ *
247
+ * @return bool
248
+ */
249
+ public function getUseIncludePath()
250
+ {
251
+ return $this->useIncludePath;
252
+ }
253
+
254
+ /**
255
+ * Turns off searching the prefix and fallback directories for classes
256
+ * that have not been registered with the class map.
257
+ *
258
+ * @param bool $classMapAuthoritative
259
+ */
260
+ public function setClassMapAuthoritative($classMapAuthoritative)
261
+ {
262
+ $this->classMapAuthoritative = $classMapAuthoritative;
263
+ }
264
+
265
+ /**
266
+ * Should class lookup fail if not found in the current class map?
267
+ *
268
+ * @return bool
269
+ */
270
+ public function isClassMapAuthoritative()
271
+ {
272
+ return $this->classMapAuthoritative;
273
+ }
274
+
275
+ /**
276
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
277
+ *
278
+ * @param string|null $apcuPrefix
279
+ */
280
+ public function setApcuPrefix($apcuPrefix)
281
+ {
282
+ $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
283
+ }
284
+
285
+ /**
286
+ * The APCu prefix in use, or null if APCu caching is not enabled.
287
+ *
288
+ * @return string|null
289
+ */
290
+ public function getApcuPrefix()
291
+ {
292
+ return $this->apcuPrefix;
293
+ }
294
+
295
+ /**
296
+ * Registers this instance as an autoloader.
297
+ *
298
+ * @param bool $prepend Whether to prepend the autoloader or not
299
+ */
300
+ public function register($prepend = false)
301
+ {
302
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
303
+ }
304
+
305
+ /**
306
+ * Unregisters this instance as an autoloader.
307
+ */
308
+ public function unregister()
309
+ {
310
+ spl_autoload_unregister(array($this, 'loadClass'));
311
+ }
312
+
313
+ /**
314
+ * Loads the given class or interface.
315
+ *
316
+ * @param string $class The name of the class
317
+ * @return bool|null True if loaded, null otherwise
318
+ */
319
+ public function loadClass($class)
320
+ {
321
+ if ($file = $this->findFile($class)) {
322
+ includeFile($file);
323
+
324
+ return true;
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Finds the path to the file where the class is defined.
330
+ *
331
+ * @param string $class The name of the class
332
+ *
333
+ * @return string|false The path if found, false otherwise
334
+ */
335
+ public function findFile($class)
336
+ {
337
+ // class map lookup
338
+ if (isset($this->classMap[$class])) {
339
+ return $this->classMap[$class];
340
+ }
341
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
342
+ return false;
343
+ }
344
+ if (null !== $this->apcuPrefix) {
345
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
346
+ if ($hit) {
347
+ return $file;
348
+ }
349
+ }
350
+
351
+ $file = $this->findFileWithExtension($class, '.php');
352
+
353
+ // Search for Hack files if we are running on HHVM
354
+ if (false === $file && defined('HHVM_VERSION')) {
355
+ $file = $this->findFileWithExtension($class, '.hh');
356
+ }
357
+
358
+ if (null !== $this->apcuPrefix) {
359
+ apcu_add($this->apcuPrefix.$class, $file);
360
+ }
361
+
362
+ if (false === $file) {
363
+ // Remember that this class does not exist.
364
+ $this->missingClasses[$class] = true;
365
+ }
366
+
367
+ return $file;
368
+ }
369
+
370
+ private function findFileWithExtension($class, $ext)
371
+ {
372
+ // PSR-4 lookup
373
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
374
+
375
+ $first = $class[0];
376
+ if (isset($this->prefixLengthsPsr4[$first])) {
377
+ $subPath = $class;
378
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
379
+ $subPath = substr($subPath, 0, $lastPos);
380
+ $search = $subPath.'\\';
381
+ if (isset($this->prefixDirsPsr4[$search])) {
382
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
383
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
384
+ if (file_exists($file = $dir . $pathEnd)) {
385
+ return $file;
386
+ }
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ // PSR-4 fallback dirs
393
+ foreach ($this->fallbackDirsPsr4 as $dir) {
394
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
395
+ return $file;
396
+ }
397
+ }
398
+
399
+ // PSR-0 lookup
400
+ if (false !== $pos = strrpos($class, '\\')) {
401
+ // namespaced class name
402
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
403
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
404
+ } else {
405
+ // PEAR-like class name
406
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
407
+ }
408
+
409
+ if (isset($this->prefixesPsr0[$first])) {
410
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
411
+ if (0 === strpos($class, $prefix)) {
412
+ foreach ($dirs as $dir) {
413
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
414
+ return $file;
415
+ }
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ // PSR-0 fallback dirs
422
+ foreach ($this->fallbackDirsPsr0 as $dir) {
423
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
424
+ return $file;
425
+ }
426
+ }
427
+
428
+ // PSR-0 include paths.
429
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
430
+ return $file;
431
+ }
432
+
433
+ return false;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Scope isolated include.
439
+ *
440
+ * Prevents access to $this/self from included files.
441
+ */
442
+ function includeFile($file)
443
+ {
444
+ include $file;
445
+ }
vendor/composer/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Copyright (c) Nils Adermann, Jordi Boggiano
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished
9
+ to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
vendor/composer/autoload_classmap.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_classmap.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
vendor/composer/autoload_files.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_files.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
10
+ '72579e7bd17821bb1321b87411366eae' => $vendorDir . '/illuminate/support/helpers.php',
11
+ );
vendor/composer/autoload_namespaces.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_namespaces.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
vendor/composer/autoload_psr4.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_psr4.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
10
+ 'Illuminate\\Support\\' => array($vendorDir . '/illuminate/support'),
11
+ 'Illuminate\\Filesystem\\' => array($vendorDir . '/illuminate/filesystem'),
12
+ 'Illuminate\\Contracts\\' => array($vendorDir . '/illuminate/contracts'),
13
+ 'Illuminate\\Container\\' => array($vendorDir . '/illuminate/container'),
14
+ 'Illuminate\\Config\\' => array($vendorDir . '/illuminate/config'),
15
+ 'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Common/Inflector'),
16
+ 'Codelight\\GDPR\\' => array($baseDir . '/src'),
17
+ );
vendor/composer/autoload_real.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_real.php @generated by Composer
4
+
5
+ class ComposerAutoloaderInit96f7d562f9c6dd12a2fa032fa886e6b0
6
+ {
7
+ private static $loader;
8
+
9
+ public static function loadClassLoader($class)
10
+ {
11
+ if ('Composer\Autoload\ClassLoader' === $class) {
12
+ require __DIR__ . '/ClassLoader.php';
13
+ }
14
+ }
15
+
16
+ public static function getLoader()
17
+ {
18
+ if (null !== self::$loader) {
19
+ return self::$loader;
20
+ }
21
+
22
+ spl_autoload_register(array('ComposerAutoloaderInit96f7d562f9c6dd12a2fa032fa886e6b0', 'loadClassLoader'), true, true);
23
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInit96f7d562f9c6dd12a2fa032fa886e6b0', 'loadClassLoader'));
25
+
26
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
+ if ($useStaticLoader) {
28
+ require_once __DIR__ . '/autoload_static.php';
29
+
30
+ call_user_func(\Composer\Autoload\ComposerStaticInit96f7d562f9c6dd12a2fa032fa886e6b0::getInitializer($loader));
31
+ } else {
32
+ $map = require __DIR__ . '/autoload_namespaces.php';
33
+ foreach ($map as $namespace => $path) {
34
+ $loader->set($namespace, $path);
35
+ }
36
+
37
+ $map = require __DIR__ . '/autoload_psr4.php';
38
+ foreach ($map as $namespace => $path) {
39
+ $loader->setPsr4($namespace, $path);
40
+ }
41
+
42
+ $classMap = require __DIR__ . '/autoload_classmap.php';
43
+ if ($classMap) {
44
+ $loader->addClassMap($classMap);
45
+ }
46
+ }
47
+
48
+ $loader->register(true);
49
+
50
+ if ($useStaticLoader) {
51
+ $includeFiles = Composer\Autoload\ComposerStaticInit96f7d562f9c6dd12a2fa032fa886e6b0::$files;
52
+ } else {
53
+ $includeFiles = require __DIR__ . '/autoload_files.php';
54
+ }
55
+ foreach ($includeFiles as $fileIdentifier => $file) {
56
+ composerRequire96f7d562f9c6dd12a2fa032fa886e6b0($fileIdentifier, $file);
57
+ }
58
+
59
+ return $loader;
60
+ }
61
+ }
62
+
63
+ function composerRequire96f7d562f9c6dd12a2fa032fa886e6b0($fileIdentifier, $file)
64
+ {
65
+ if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
66
+ require $file;
67
+
68
+ $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
69
+ }
70
+ }
vendor/composer/autoload_static.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_static.php @generated by Composer
4
+
5
+ namespace Composer\Autoload;
6
+
7
+ class ComposerStaticInit96f7d562f9c6dd12a2fa032fa886e6b0
8
+ {
9
+ public static $files = array (
10
+ '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
11
+ '72579e7bd17821bb1321b87411366eae' => __DIR__ . '/..' . '/illuminate/support/helpers.php',
12
+ );
13
+
14
+ public static $prefixLengthsPsr4 = array (
15
+ 'S' =>
16
+ array (
17
+ 'Symfony\\Component\\Finder\\' => 25,
18
+ ),
19
+ 'I' =>
20
+ array (
21
+ 'Illuminate\\Support\\' => 19,
22
+ 'Illuminate\\Filesystem\\' => 22,
23
+ 'Illuminate\\Contracts\\' => 21,
24
+ 'Illuminate\\Container\\' => 21,
25
+ 'Illuminate\\Config\\' => 18,
26
+ ),
27
+ 'D' =>
28
+ array (
29
+ 'Doctrine\\Common\\Inflector\\' => 26,
30
+ ),
31
+ 'C' =>
32
+ array (
33
+ 'Codelight\\GDPR\\' => 15,
34
+ ),
35
+ );
36
+
37
+ public static $prefixDirsPsr4 = array (
38
+ 'Symfony\\Component\\Finder\\' =>
39
+ array (
40
+ 0 => __DIR__ . '/..' . '/symfony/finder',
41
+ ),
42
+ 'Illuminate\\Support\\' =>
43
+ array (
44
+ 0 => __DIR__ . '/..' . '/illuminate/support',
45
+ ),
46
+ 'Illuminate\\Filesystem\\' =>
47
+ array (
48
+ 0 => __DIR__ . '/..' . '/illuminate/filesystem',
49
+ ),
50
+ 'Illuminate\\Contracts\\' =>
51
+ array (
52
+ 0 => __DIR__ . '/..' . '/illuminate/contracts',
53
+ ),
54
+ 'Illuminate\\Container\\' =>
55
+ array (
56
+ 0 => __DIR__ . '/..' . '/illuminate/container',
57
+ ),
58
+ 'Illuminate\\Config\\' =>
59
+ array (
60
+ 0 => __DIR__ . '/..' . '/illuminate/config',
61
+ ),
62
+ 'Doctrine\\Common\\Inflector\\' =>
63
+ array (
64
+ 0 => __DIR__ . '/..' . '/doctrine/inflector/lib/Doctrine/Common/Inflector',
65
+ ),
66
+ 'Codelight\\GDPR\\' =>
67
+ array (
68
+ 0 => __DIR__ . '/../..' . '/src',
69
+ ),
70
+ );
71
+
72
+ public static function getInitializer(ClassLoader $loader)
73
+ {
74
+ return \Closure::bind(function () use ($loader) {
75
+ $loader->prefixLengthsPsr4 = ComposerStaticInit96f7d562f9c6dd12a2fa032fa886e6b0::$prefixLengthsPsr4;
76
+ $loader->prefixDirsPsr4 = ComposerStaticInit96f7d562f9c6dd12a2fa032fa886e6b0::$prefixDirsPsr4;
77
+
78
+ }, null, ClassLoader::class);
79
+ }
80
+ }
vendor/composer/installed.json ADDED
@@ -0,0 +1,419 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "name": "doctrine/inflector",
4
+ "version": "v1.3.0",
5
+ "version_normalized": "1.3.0.0",
6
+ "source": {
7
+ "type": "git",
8
+ "url": "https://github.com/doctrine/inflector.git",
9
+ "reference": "5527a48b7313d15261292c149e55e26eae771b0a"
10
+ },
11
+ "dist": {
12
+ "type": "zip",
13
+ "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a",
14
+ "reference": "5527a48b7313d15261292c149e55e26eae771b0a",
15
+ "shasum": ""
16
+ },
17
+ "require": {
18
+ "php": "^7.1"
19
+ },
20
+ "require-dev": {
21
+ "phpunit/phpunit": "^6.2"
22
+ },
23
+ "time": "2018-01-09T20:05:19+00:00",
24
+ "type": "library",
25
+ "extra": {
26
+ "branch-alias": {
27
+ "dev-master": "1.3.x-dev"
28
+ }
29
+ },
30
+ "installation-source": "dist",
31
+ "autoload": {
32
+ "psr-4": {
33
+ "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector"
34
+ }
35
+ },
36
+ "notification-url": "https://packagist.org/downloads/",
37
+ "license": [
38
+ "MIT"
39
+ ],
40
+ "authors": [
41
+ {
42
+ "name": "Roman Borschel",
43
+ "email": "roman@code-factory.org"
44
+ },
45
+ {
46
+ "name": "Benjamin Eberlei",
47
+ "email": "kontakt@beberlei.de"
48
+ },
49
+ {
50
+ "name": "Guilherme Blanco",
51
+ "email": "guilhermeblanco@gmail.com"
52
+ },
53
+ {
54
+ "name": "Jonathan Wage",
55
+ "email": "jonwage@gmail.com"
56
+ },
57
+ {
58
+ "name": "Johannes Schmitt",
59
+ "email": "schmittjoh@gmail.com"
60
+ }
61
+ ],
62
+ "description": "Common String Manipulations with regard to casing and singular/plural rules.",
63
+ "homepage": "http://www.doctrine-project.org",
64
+ "keywords": [
65
+ "inflection",
66
+ "pluralize",
67
+ "singularize",
68
+ "string"
69
+ ]
70
+ },
71
+ {
72
+ "name": "illuminate/config",
73
+ "version": "v5.4.0",
74
+ "version_normalized": "5.4.0.0",
75
+ "source": {
76
+ "type": "git",
77
+ "url": "https://github.com/illuminate/config.git",
78
+ "reference": "6a1a4f2c8b0732a419f5df018b8a2f59cebbed8f"
79
+ },
80
+ "dist": {
81
+ "type": "zip",
82
+ "url": "https://api.github.com/repos/illuminate/config/zipball/6a1a4f2c8b0732a419f5df018b8a2f59cebbed8f",
83
+ "reference": "6a1a4f2c8b0732a419f5df018b8a2f59cebbed8f",
84
+ "shasum": ""
85
+ },
86
+ "require": {
87
+ "illuminate/contracts": "5.4.*",
88
+ "illuminate/filesystem": "5.4.*",
89
+ "illuminate/support": "5.4.*",
90
+ "php": ">=5.6.4"
91
+ },
92
+ "time": "2016-12-23T13:31:56+00:00",
93
+ "type": "library",
94
+ "extra": {
95
+ "branch-alias": {
96
+ "dev-master": "5.4-dev"
97
+ }
98
+ },
99
+ "installation-source": "dist",
100
+ "autoload": {
101
+ "psr-4": {
102
+ "Illuminate\\Config\\": ""
103
+ }
104
+ },
105
+ "notification-url": "https://packagist.org/downloads/",
106
+ "license": [
107
+ "MIT"
108
+ ],
109
+ "authors": [
110
+ {
111
+ "name": "Taylor Otwell",
112
+ "email": "taylor@laravel.com"
113
+ }
114
+ ],
115
+ "description": "The Illuminate Config package.",
116
+ "homepage": "https://laravel.com"
117
+ },
118
+ {
119
+ "name": "illuminate/container",
120
+ "version": "v5.4.36",
121
+ "version_normalized": "5.4.36.0",
122
+ "source": {
123
+ "type": "git",
124
+ "url": "https://github.com/illuminate/container.git",
125
+ "reference": "c5b8a02a34a52c307f16922334c355c5eef725a6"
126
+ },
127
+ "dist": {
128
+ "type": "zip",
129
+ "url": "https://api.github.com/repos/illuminate/container/zipball/c5b8a02a34a52c307f16922334c355c5eef725a6",
130
+ "reference": "c5b8a02a34a52c307f16922334c355c5eef725a6",
131
+ "shasum": ""
132
+ },
133
+ "require": {
134
+ "illuminate/contracts": "5.4.*",
135
+ "php": ">=5.6.4"
136
+ },
137
+ "time": "2017-05-24T14:15:53+00:00",
138
+ "type": "library",
139
+ "extra": {
140
+ "branch-alias": {
141
+ "dev-master": "5.4-dev"
142
+ }
143
+ },
144
+ "installation-source": "dist",
145
+ "autoload": {
146
+ "psr-4": {
147
+ "Illuminate\\Container\\": ""
148
+ }
149
+ },
150
+ "notification-url": "https://packagist.org/downloads/",
151
+ "license": [
152
+ "MIT"
153
+ ],
154
+ "authors": [
155
+ {
156
+ "name": "Taylor Otwell",
157
+ "email": "taylor@laravel.com"
158
+ }
159
+ ],
160
+ "description": "The Illuminate Container package.",
161
+ "homepage": "https://laravel.com"
162
+ },
163
+ {
164
+ "name": "illuminate/contracts",
165
+ "version": "v5.4.36",
166
+ "version_normalized": "5.4.36.0",
167
+ "source": {
168
+ "type": "git",
169
+ "url": "https://github.com/illuminate/contracts.git",
170
+ "reference": "67f642e018f3e95fb0b2ebffc206c3200391b1ab"
171
+ },
172
+ "dist": {
173
+ "type": "zip",
174
+ "url": "https://api.github.com/repos/illuminate/contracts/zipball/67f642e018f3e95fb0b2ebffc206c3200391b1ab",
175
+ "reference": "67f642e018f3e95fb0b2ebffc206c3200391b1ab",
176
+ "shasum": ""
177
+ },
178
+ "require": {
179
+ "php": ">=5.6.4"
180
+ },
181
+ "time": "2017-08-26T23:56:53+00:00",
182
+ "type": "library",
183
+ "extra": {
184
+ "branch-alias": {
185
+ "dev-master": "5.4-dev"
186
+ }
187
+ },
188
+ "installation-source": "dist",
189
+ "autoload": {
190
+ "psr-4": {
191
+ "Illuminate\\Contracts\\": ""
192
+ }
193
+ },
194
+ "notification-url": "https://packagist.org/downloads/",
195
+ "license": [
196
+ "MIT"
197
+ ],
198
+ "authors": [
199
+ {
200
+ "name": "Taylor Otwell",
201
+ "email": "taylor@laravel.com"
202
+ }
203
+ ],
204
+ "description": "The Illuminate Contracts package.",
205
+ "homepage": "https://laravel.com"
206
+ },
207
+ {
208
+ "name": "illuminate/filesystem",
209
+ "version": "v5.4.36",
210
+ "version_normalized": "5.4.36.0",
211
+ "source": {
212
+ "type": "git",
213
+ "url": "https://github.com/illuminate/filesystem.git",
214
+ "reference": "b800a1423d06869ee5c2768eee123917f12b693e"
215
+ },
216
+ "dist": {
217
+ "type": "zip",
218
+ "url": "https://api.github.com/repos/illuminate/filesystem/zipball/b800a1423d06869ee5c2768eee123917f12b693e",
219
+ "reference": "b800a1423d06869ee5c2768eee123917f12b693e",
220
+ "shasum": ""
221
+ },
222
+ "require": {
223
+ "illuminate/contracts": "5.4.*",
224
+ "illuminate/support": "5.4.*",
225
+ "php": ">=5.6.4",
226
+ "symfony/finder": "~3.2"
227
+ },
228
+ "suggest": {
229
+ "league/flysystem": "Required to use the Flysystem local and FTP drivers (~1.0).",
230
+ "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (~1.0).",
231
+ "league/flysystem-rackspace": "Required to use the Flysystem Rackspace driver (~1.0)."
232
+ },
233
+ "time": "2017-08-02T21:58:00+00:00",
234
+ "type": "library",
235
+ "extra": {
236
+ "branch-alias": {
237
+ "dev-master": "5.4-dev"
238
+ }
239
+ },
240
+ "installation-source": "dist",
241
+ "autoload": {
242
+ "psr-4": {
243
+ "Illuminate\\Filesystem\\": ""
244
+ }
245
+ },
246
+ "notification-url": "https://packagist.org/downloads/",
247
+ "license": [
248
+ "MIT"
249
+ ],
250
+ "authors": [
251
+ {
252
+ "name": "Taylor Otwell",
253
+ "email": "taylor@laravel.com"
254
+ }
255
+ ],
256
+ "description": "The Illuminate Filesystem package.",
257
+ "homepage": "https://laravel.com"
258
+ },
259
+ {
260
+ "name": "illuminate/support",
261
+ "version": "v5.4.36",
262
+ "version_normalized": "5.4.36.0",
263
+ "source": {
264
+ "type": "git",
265
+ "url": "https://github.com/illuminate/support.git",
266
+ "reference": "feab1d1495fd6d38970bd6c83586ba2ace8f299a"
267
+ },
268
+ "dist": {
269
+ "type": "zip",
270
+ "url": "https://api.github.com/repos/illuminate/support/zipball/feab1d1495fd6d38970bd6c83586ba2ace8f299a",
271
+ "reference": "feab1d1495fd6d38970bd6c83586ba2ace8f299a",
272
+ "shasum": ""
273
+ },
274
+ "require": {
275
+ "doctrine/inflector": "~1.1",
276
+ "ext-mbstring": "*",
277
+ "illuminate/contracts": "5.4.*",
278
+ "paragonie/random_compat": "~1.4|~2.0",
279
+ "php": ">=5.6.4"
280
+ },
281
+ "replace": {
282
+ "tightenco/collect": "self.version"
283
+ },
284
+ "suggest": {
285
+ "illuminate/filesystem": "Required to use the composer class (5.2.*).",
286
+ "symfony/process": "Required to use the composer class (~3.2).",
287
+ "symfony/var-dumper": "Required to use the dd function (~3.2)."
288
+ },
289
+ "time": "2017-08-15T13:25:41+00:00",
290
+ "type": "library",
291
+ "extra": {
292
+ "branch-alias": {
293
+ "dev-master": "5.4-dev"
294
+ }
295
+ },
296
+ "installation-source": "dist",
297
+ "autoload": {
298
+ "psr-4": {
299
+ "Illuminate\\Support\\": ""
300
+ },
301
+ "files": [
302
+ "helpers.php"
303
+ ]
304
+ },
305
+ "notification-url": "https://packagist.org/downloads/",
306
+ "license": [
307
+ "MIT"
308
+ ],
309
+ "authors": [
310
+ {
311
+ "name": "Taylor Otwell",
312
+ "email": "taylor@laravel.com"
313
+ }
314
+ ],
315
+ "description": "The Illuminate Support package.",
316
+ "homepage": "https://laravel.com"
317
+ },
318
+ {
319
+ "name": "paragonie/random_compat",
320
+ "version": "v2.0.11",
321
+ "version_normalized": "2.0.11.0",
322
+ "source": {
323
+ "type": "git",
324
+ "url": "https://github.com/paragonie/random_compat.git",
325
+ "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8"
326
+ },
327
+ "dist": {
328
+ "type": "zip",
329
+ "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8",
330
+ "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8",
331
+ "shasum": ""
332
+ },
333
+ "require": {
334
+ "php": ">=5.2.0"
335
+ },
336
+ "require-dev": {
337
+ "phpunit/phpunit": "4.*|5.*"
338
+ },
339
+ "suggest": {
340
+ "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
341
+ },
342
+ "time": "2017-09-27T21:40:39+00:00",
343
+ "type": "library",
344
+ "installation-source": "dist",
345
+ "autoload": {
346
+ "files": [
347
+ "lib/random.php"
348
+ ]
349
+ },
350
+ "notification-url": "https://packagist.org/downloads/",
351
+ "license": [
352
+ "MIT"
353
+ ],
354
+ "authors": [
355
+ {
356
+ "name": "Paragon Initiative Enterprises",
357
+ "email": "security@paragonie.com",
358
+ "homepage": "https://paragonie.com"
359
+ }
360
+ ],
361
+ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
362
+ "keywords": [
363
+ "csprng",
364
+ "pseudorandom",
365
+ "random"
366
+ ]
367
+ },
368
+ {
369
+ "name": "symfony/finder",
370
+ "version": "v3.4.6",
371
+ "version_normalized": "3.4.6.0",
372
+ "source": {
373
+ "type": "git",
374
+ "url": "https://github.com/symfony/finder.git",
375
+ "reference": "a479817ce0a9e4adfd7d39c6407c95d97c254625"
376
+ },
377
+ "dist": {
378
+ "type": "zip",
379
+ "url": "https://api.github.com/repos/symfony/finder/zipball/a479817ce0a9e4adfd7d39c6407c95d97c254625",
380
+ "reference": "a479817ce0a9e4adfd7d39c6407c95d97c254625",
381
+ "shasum": ""
382
+ },
383
+ "require": {
384
+ "php": "^5.5.9|>=7.0.8"
385
+ },
386
+ "time": "2018-03-05T18:28:11+00:00",
387
+ "type": "library",
388
+ "extra": {
389
+ "branch-alias": {
390
+ "dev-master": "3.4-dev"
391
+ }
392
+ },
393
+ "installation-source": "dist",
394
+ "autoload": {
395
+ "psr-4": {
396
+ "Symfony\\Component\\Finder\\": ""
397
+ },
398
+ "exclude-from-classmap": [
399
+ "/Tests/"
400
+ ]
401
+ },
402
+ "notification-url": "https://packagist.org/downloads/",
403
+ "license": [
404
+ "MIT"
405
+ ],
406
+ "authors": [
407
+ {
408
+ "name": "Fabien Potencier",
409
+ "email": "fabien@symfony.com"
410
+ },
411
+ {
412
+ "name": "Symfony Community",
413
+ "homepage": "https://symfony.com/contributors"
414
+ }
415
+ ],
416
+ "description": "Symfony Finder Component",
417
+ "homepage": "https://symfony.com"
418
+ }
419
+ ]
vendor/doctrine/inflector/LICENSE ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2006-2015 Doctrine Project
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
vendor/doctrine/inflector/README.md ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ # Doctrine Inflector
2
+
3
+ Doctrine Inflector is a small library that can perform string manipulations
4
+ with regard to upper-/lowercase and singular/plural forms of words.
5
+
6
+ [![Build Status](https://travis-ci.org/doctrine/inflector.svg?branch=master)](https://travis-ci.org/doctrine/inflector)
vendor/doctrine/inflector/composer.json ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "doctrine/inflector",
3
+ "type": "library",
4
+ "description": "Common String Manipulations with regard to casing and singular/plural rules.",
5
+ "keywords": ["string", "inflection", "singularize", "pluralize"],
6
+ "homepage": "http://www.doctrine-project.org",
7
+ "license": "MIT",
8
+ "authors": [
9
+ {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
10
+ {"name": "Roman Borschel", "email": "roman@code-factory.org"},
11
+ {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
12
+ {"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
13
+ {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
14
+ ],
15
+ "require": {
16
+ "php": "^7.1"
17
+ },
18
+ "require-dev": {
19
+ "phpunit/phpunit": "^6.2"
20
+ },
21
+ "autoload": {
22
+ "psr-4": { "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" }
23
+ },
24
+ "autoload-dev": {
25
+ "psr-4": { "Doctrine\\Tests\\Common\\Inflector\\": "tests/Doctrine/Tests/Common/Inflector" }
26
+ },
27
+ "extra": {
28
+ "branch-alias": {
29
+ "dev-master": "1.3.x-dev"
30
+ }
31
+ }
32
+ }
vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php ADDED
@@ -0,0 +1,490 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
+ *
15
+ * This software consists of voluntary contributions made by many individuals
16
+ * and is licensed under the MIT license. For more information, see
17
+ * <http://www.doctrine-project.org>.
18
+ */
19
+
20
+ namespace Doctrine\Common\Inflector;
21
+
22
+ /**
23
+ * Doctrine inflector has static methods for inflecting text.
24
+ *
25
+ * The methods in these classes are from several different sources collected
26
+ * across several different php projects and several different authors. The
27
+ * original author names and emails are not known.
28
+ *
29
+ * Pluralize & Singularize implementation are borrowed from CakePHP with some modifications.
30
+ *
31
+ * @link www.doctrine-project.org
32
+ * @since 1.0
33
+ * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
34
+ * @author Jonathan H. Wage <jonwage@gmail.com>
35
+ */
36
+ class Inflector
37
+ {
38
+ /**
39
+ * Plural inflector rules.
40
+ *
41
+ * @var string[][]
42
+ */
43
+ private static $plural = array(
44
+ 'rules' => array(
45
+ '/(s)tatus$/i' => '\1\2tatuses',
46
+ '/(quiz)$/i' => '\1zes',
47
+ '/^(ox)$/i' => '\1\2en',
48
+ '/([m|l])ouse$/i' => '\1ice',
49
+ '/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
50
+ '/(x|ch|ss|sh)$/i' => '\1es',
51
+ '/([^aeiouy]|qu)y$/i' => '\1ies',
52
+ '/(hive|gulf)$/i' => '\1s',
53
+ '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
54
+ '/sis$/i' => 'ses',
55
+ '/([ti])um$/i' => '\1a',
56
+ '/(c)riterion$/i' => '\1riteria',
57
+ '/(p)erson$/i' => '\1eople',
58
+ '/(m)an$/i' => '\1en',
59
+ '/(c)hild$/i' => '\1hildren',
60
+ '/(f)oot$/i' => '\1eet',
61
+ '/(buffal|her|potat|tomat|volcan)o$/i' => '\1\2oes',
62
+ '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
63
+ '/us$/i' => 'uses',
64
+ '/(alias)$/i' => '\1es',
65
+ '/(analys|ax|cris|test|thes)is$/i' => '\1es',
66
+ '/s$/' => 's',
67
+ '/^$/' => '',
68
+ '/$/' => 's',
69
+ ),
70
+ 'uninflected' => array(
71
+ '.*[nrlm]ese',
72
+ '.*deer',
73
+ '.*fish',
74
+ '.*measles',
75
+ '.*ois',
76
+ '.*pox',
77
+ '.*sheep',
78
+ 'people',
79
+ 'cookie',
80
+ 'police',
81
+ ),
82
+ 'irregular' => array(
83
+ 'atlas' => 'atlases',
84
+ 'axe' => 'axes',
85
+ 'beef' => 'beefs',
86
+ 'brother' => 'brothers',
87
+ 'cafe' => 'cafes',
88
+ 'chateau' => 'chateaux',
89
+ 'niveau' => 'niveaux',
90
+ 'child' => 'children',
91
+ 'cookie' => 'cookies',
92
+ 'corpus' => 'corpuses',
93
+ 'cow' => 'cows',
94
+ 'criterion' => 'criteria',
95
+ 'curriculum' => 'curricula',
96
+ 'demo' => 'demos',
97
+ 'domino' => 'dominoes',
98
+ 'echo' => 'echoes',
99
+ 'foot' => 'feet',
100
+ 'fungus' => 'fungi',
101
+ 'ganglion' => 'ganglions',
102
+ 'genie' => 'genies',
103
+ 'genus' => 'genera',
104
+ 'goose' => 'geese',
105
+ 'graffito' => 'graffiti',
106
+ 'hippopotamus' => 'hippopotami',
107
+ 'hoof' => 'hoofs',
108
+ 'human' => 'humans',
109
+ 'iris' => 'irises',
110
+ 'larva' => 'larvae',
111
+ 'leaf' => 'leaves',
112
+ 'loaf' => 'loaves',
113
+ 'man' => 'men',
114
+ 'medium' => 'media',
115
+ 'memorandum' => 'memoranda',
116
+ 'money' => 'monies',
117
+ 'mongoose' => 'mongooses',
118
+ 'motto' => 'mottoes',
119
+ 'move' => 'moves',
120
+ 'mythos' => 'mythoi',
121
+ 'niche' => 'niches',
122
+ 'nucleus' => 'nuclei',
123
+ 'numen' => 'numina',
124
+ 'occiput' => 'occiputs',
125
+ 'octopus' => 'octopuses',
126
+ 'opus' => 'opuses',
127
+ 'ox' => 'oxen',
128
+ 'passerby' => 'passersby',
129
+ 'penis' => 'penises',
130
+ 'person' => 'people',
131
+ 'plateau' => 'plateaux',
132
+ 'runner-up' => 'runners-up',
133
+ 'sex' => 'sexes',
134
+ 'soliloquy' => 'soliloquies',
135
+ 'son-in-law' => 'sons-in-law',
136
+ 'syllabus' => 'syllabi',
137
+ 'testis' => 'testes',
138
+ 'thief' => 'thieves',
139
+ 'tooth' => 'teeth',
140
+ 'tornado' => 'tornadoes',
141
+ 'trilby' => 'trilbys',
142
+ 'turf' => 'turfs',
143
+ 'valve' => 'valves',
144
+ 'volcano' => 'volcanoes',
145
+ )
146
+ );
147
+
148
+ /**
149
+ * Singular inflector rules.
150
+ *
151
+ * @var string[][]
152
+ */
153
+ private static $singular = array(
154
+ 'rules' => array(
155
+ '/(s)tatuses$/i' => '\1\2tatus',
156
+ '/^(.*)(menu)s$/i' => '\1\2',
157
+ '/(quiz)zes$/i' => '\\1',
158
+ '/(matr)ices$/i' => '\1ix',
159
+ '/(vert|ind)ices$/i' => '\1ex',
160
+ '/^(ox)en/i' => '\1',
161
+ '/(alias)(es)*$/i' => '\1',
162
+ '/(buffal|her|potat|tomat|volcan)oes$/i' => '\1o',
163
+ '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
164
+ '/([ftw]ax)es/i' => '\1',
165
+ '/(analys|ax|cris|test|thes)es$/i' => '\1is',
166
+ '/(shoe|slave)s$/i' => '\1',
167
+ '/(o)es$/i' => '\1',
168
+ '/ouses$/' => 'ouse',
169
+ '/([^a])uses$/' => '\1us',
170
+ '/([m|l])ice$/i' => '\1ouse',
171
+ '/(x|ch|ss|sh)es$/i' => '\1',
172
+ '/(m)ovies$/i' => '\1\2ovie',
173
+ '/(s)eries$/i' => '\1\2eries',
174
+ '/([^aeiouy]|qu)ies$/i' => '\1y',
175
+ '/([lr])ves$/i' => '\1f',
176
+ '/(tive)s$/i' => '\1',
177
+ '/(hive)s$/i' => '\1',
178
+ '/(drive)s$/i' => '\1',
179
+ '/(dive)s$/i' => '\1',
180
+ '/(olive)s$/i' => '\1',
181
+ '/([^fo])ves$/i' => '\1fe',
182
+ '/(^analy)ses$/i' => '\1sis',
183
+ '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
184
+ '/(c)riteria$/i' => '\1riterion',
185
+ '/([ti])a$/i' => '\1um',
186
+ '/(p)eople$/i' => '\1\2erson',
187
+ '/(m)en$/i' => '\1an',
188
+ '/(c)hildren$/i' => '\1\2hild',
189
+ '/(f)eet$/i' => '\1oot',
190
+ '/(n)ews$/i' => '\1\2ews',
191
+ '/eaus$/' => 'eau',
192
+ '/^(.*us)$/' => '\\1',
193
+ '/s$/i' => '',
194
+ ),
195
+ 'uninflected' => array(
196
+ '.*[nrlm]ese',
197
+ '.*deer',
198
+ '.*fish',
199
+ '.*measles',
200
+ '.*ois',
201
+ '.*pox',
202
+ '.*sheep',
203
+ '.*ss',
204
+ 'data',
205
+ 'police',
206
+ 'pants',
207
+ 'clothes',
208
+ ),
209
+ 'irregular' => array(
210
+ 'abuses' => 'abuse',
211
+ 'avalanches' => 'avalanche',
212
+ 'caches' => 'cache',
213
+ 'criteria' => 'criterion',
214
+ 'curves' => 'curve',
215
+ 'emphases' => 'emphasis',
216
+ 'foes' => 'foe',
217
+ 'geese' => 'goose',
218
+ 'graves' => 'grave',
219
+ 'hoaxes' => 'hoax',
220
+ 'media' => 'medium',
221
+ 'neuroses' => 'neurosis',
222
+ 'waves' => 'wave',
223
+ 'oases' => 'oasis',
224
+ 'valves' => 'valve',
225
+ )
226
+ );
227
+
228
+ /**
229
+ * Words that should not be inflected.
230
+ *
231
+ * @var array
232
+ */
233
+ private static $uninflected = array(
234
+ '.*?media', 'Amoyese', 'audio', 'bison', 'Borghese', 'bream', 'breeches',
235
+ 'britches', 'buffalo', 'cantus', 'carp', 'chassis', 'clippers', 'cod', 'coitus', 'compensation', 'Congoese',
236
+ 'contretemps', 'coreopsis', 'corps', 'data', 'debris', 'deer', 'diabetes', 'djinn', 'education', 'eland',
237
+ 'elk', 'emoji', 'equipment', 'evidence', 'Faroese', 'feedback', 'fish', 'flounder', 'Foochowese',
238
+ 'Furniture', 'furniture', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'gold',
239
+ 'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', 'jackanapes', 'jedi',
240
+ 'Kiplingese', 'knowledge', 'Kongoese', 'love', 'Lucchese', 'Luggage', 'mackerel', 'Maltese', 'metadata',
241
+ 'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese', 'nutrition', 'offspring',
242
+ 'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'plankton', 'pliers', 'pokemon', 'police', 'Portuguese',
243
+ 'proceedings', 'rabies', 'rain', 'rhinoceros', 'rice', 'salmon', 'Sarawakese', 'scissors', 'sea[- ]bass',
244
+ 'series', 'Shavese', 'shears', 'sheep', 'siemens', 'species', 'staff', 'swine', 'traffic',
245
+ 'trousers', 'trout', 'tuna', 'us', 'Vermontese', 'Wenchowese', 'wheat', 'whiting', 'wildebeest', 'Yengeese'
246
+ );
247
+
248
+ /**
249
+ * Method cache array.
250
+ *
251
+ * @var array
252
+ */
253
+ private static $cache = array();
254
+
255
+ /**
256
+ * The initial state of Inflector so reset() works.
257
+ *
258
+ * @var array
259
+ */
260
+ private static $initialState = array();
261
+
262
+ /**
263
+ * Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'.
264
+ */
265
+ public static function tableize(string $word) : string
266
+ {
267
+ return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word));
268
+ }
269
+
270
+ /**
271
+ * Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'.
272
+ */
273
+ public static function classify(string $word) : string
274
+ {
275
+ return str_replace([' ', '_', '-'], '', ucwords($word, ' _-'));
276
+ }
277
+
278
+ /**
279
+ * Camelizes a word. This uses the classify() method and turns the first character to lowercase.
280
+ */
281
+ public static function camelize(string $word) : string
282
+ {
283
+ return lcfirst(self::classify($word));
284
+ }
285
+
286
+ /**
287
+ * Uppercases words with configurable delimeters between words.
288
+ *
289
+ * Takes a string and capitalizes all of the words, like PHP's built-in
290
+ * ucwords function. This extends that behavior, however, by allowing the
291
+ * word delimeters to be configured, rather than only separating on
292
+ * whitespace.
293
+ *
294
+ * Here is an example:
295
+ * <code>
296
+ * <?php
297
+ * $string = 'top-o-the-morning to all_of_you!';
298
+ * echo \Doctrine\Common\Inflector\Inflector::ucwords($string);
299
+ * // Top-O-The-Morning To All_of_you!
300
+ *
301
+ * echo \Doctrine\Common\Inflector\Inflector::ucwords($string, '-_ ');
302
+ * // Top-O-The-Morning To All_Of_You!
303
+ * ?>
304
+ * </code>
305
+ *
306
+ * @param string $string The string to operate on.
307
+ * @param string $delimiters A list of word separators.
308
+ *
309
+ * @return string The string with all delimeter-separated words capitalized.
310
+ */
311
+ public static function ucwords(string $string, string $delimiters = " \n\t\r\0\x0B-") : string
312
+ {
313
+ return ucwords($string, $delimiters);
314
+ }
315
+
316
+ /**
317
+ * Clears Inflectors inflected value caches, and resets the inflection
318
+ * rules to the initial values.
319
+ */
320
+ public static function reset() : void
321
+ {
322
+ if (empty(self::$initialState)) {
323
+ self::$initialState = get_class_vars('Inflector');
324
+
325
+ return;
326
+ }
327
+
328
+ foreach (self::$initialState as $key => $val) {
329
+ if ($key !== 'initialState') {
330
+ self::${$key} = $val;
331
+ }
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Adds custom inflection $rules, of either 'plural' or 'singular' $type.
337
+ *
338
+ * ### Usage:
339
+ *
340
+ * {{{
341
+ * Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables'));
342
+ * Inflector::rules('plural', array(
343
+ * 'rules' => array('/^(inflect)ors$/i' => '\1ables'),
344
+ * 'uninflected' => array('dontinflectme'),
345
+ * 'irregular' => array('red' => 'redlings')
346
+ * ));
347
+ * }}}
348
+ *
349
+ * @param string $type The type of inflection, either 'plural' or 'singular'
350
+ * @param array|iterable $rules An array of rules to be added.
351
+ * @param boolean $reset If true, will unset default inflections for all
352
+ * new rules that are being defined in $rules.
353
+ *
354
+ * @return void
355
+ */
356
+ public static function rules(string $type, iterable $rules, bool $reset = false) : void
357
+ {
358
+ foreach ($rules as $rule => $pattern) {
359
+ if ( ! is_array($pattern)) {
360
+ continue;
361
+ }
362
+
363
+ if ($reset) {
364
+ self::${$type}[$rule] = $pattern;
365
+ } else {
366
+ self::${$type}[$rule] = ($rule === 'uninflected')
367
+ ? array_merge($pattern, self::${$type}[$rule])
368
+ : $pattern + self::${$type}[$rule];
369
+ }
370
+
371
+ unset($rules[$rule], self::${$type}['cache' . ucfirst($rule)]);
372
+
373
+ if (isset(self::${$type}['merged'][$rule])) {
374
+ unset(self::${$type}['merged'][$rule]);
375
+ }
376
+
377
+ if ($type === 'plural') {
378
+ self::$cache['pluralize'] = self::$cache['tableize'] = array();
379
+ } elseif ($type === 'singular') {
380
+ self::$cache['singularize'] = array();
381
+ }
382
+ }
383
+
384
+ self::${$type}['rules'] = $rules + self::${$type}['rules'];
385
+ }
386
+
387
+ /**
388
+ * Returns a word in plural form.
389
+ *
390
+ * @param string $word The word in singular form.
391
+ *
392
+ * @return string The word in plural form.
393
+ */
394
+ public static function pluralize(string $word) : string
395
+ {
396
+ if (isset(self::$cache['pluralize'][$word])) {
397
+ return self::$cache['pluralize'][$word];
398
+ }
399
+
400
+ if (!isset(self::$plural['merged']['irregular'])) {
401
+ self::$plural['merged']['irregular'] = self::$plural['irregular'];
402
+ }
403
+
404
+ if (!isset(self::$plural['merged']['uninflected'])) {
405
+ self::$plural['merged']['uninflected'] = array_merge(self::$plural['uninflected'], self::$uninflected);
406
+ }
407
+
408
+ if (!isset(self::$plural['cacheUninflected']) || !isset(self::$plural['cacheIrregular'])) {
409
+ self::$plural['cacheUninflected'] = '(?:' . implode('|', self::$plural['merged']['uninflected']) . ')';
410
+ self::$plural['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$plural['merged']['irregular'])) . ')';
411
+ }
412
+
413
+ if (preg_match('/(.*)\\b(' . self::$plural['cacheIrregular'] . ')$/i', $word, $regs)) {
414
+ self::$cache['pluralize'][$word] = $regs[1] . $word[0] . substr(self::$plural['merged']['irregular'][strtolower($regs[2])], 1);
415
+
416
+ return self::$cache['pluralize'][$word];
417
+ }
418
+
419
+ if (preg_match('/^(' . self::$plural['cacheUninflected'] . ')$/i', $word, $regs)) {
420
+ self::$cache['pluralize'][$word] = $word;
421
+
422
+ return $word;
423
+ }
424
+
425
+ foreach (self::$plural['rules'] as $rule => $replacement) {
426
+ if (preg_match($rule, $word)) {
427
+ self::$cache['pluralize'][$word] = preg_replace($rule, $replacement, $word);
428
+
429
+ return self::$cache['pluralize'][$word];
430
+ }
431
+ }
432
+ }
433
+
434
+ /**
435
+ * Returns a word in singular form.
436
+ *
437
+ * @param string $word The word in plural form.
438
+ *
439
+ * @return string The word in singular form.
440
+ */
441
+ public static function singularize(string $word) : string
442
+ {
443
+ if (isset(self::$cache['singularize'][$word])) {
444
+ return self::$cache['singularize'][$word];
445
+ }
446
+
447
+ if (!isset(self::$singular['merged']['uninflected'])) {
448
+ self::$singular['merged']['uninflected'] = array_merge(
449
+ self::$singular['uninflected'],
450
+ self::$uninflected
451
+ );
452
+ }
453
+
454
+ if (!isset(self::$singular['merged']['irregular'])) {
455
+ self::$singular['merged']['irregular'] = array_merge(
456
+ self::$singular['irregular'],
457
+ array_flip(self::$plural['irregular'])
458
+ );
459
+ }
460
+
461
+ if (!isset(self::$singular['cacheUninflected']) || !isset(self::$singular['cacheIrregular'])) {
462
+ self::$singular['cacheUninflected'] = '(?:' . implode('|', self::$singular['merged']['uninflected']) . ')';
463
+ self::$singular['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$singular['merged']['irregular'])) . ')';
464
+ }
465
+
466
+ if (preg_match('/(.*)\\b(' . self::$singular['cacheIrregular'] . ')$/i', $word, $regs)) {
467
+ self::$cache['singularize'][$word] = $regs[1] . $word[0] . substr(self::$singular['merged']['irregular'][strtolower($regs[2])], 1);
468
+
469
+ return self::$cache['singularize'][$word];
470
+ }
471
+
472
+ if (preg_match('/^(' . self::$singular['cacheUninflected'] . ')$/i', $word, $regs)) {
473
+ self::$cache['singularize'][$word] = $word;
474
+
475
+ return $word;
476
+ }
477
+
478
+ foreach (self::$singular['rules'] as $rule => $replacement) {
479
+ if (preg_match($rule, $word)) {
480
+ self::$cache['singularize'][$word] = preg_replace($rule, $replacement, $word);
481
+
482
+ return self::$cache['singularize'][$word];
483
+ }
484
+ }
485
+
486
+ self::$cache['singularize'][$word] = $word;
487
+
488
+ return $word;
489
+ }
490
+ }
vendor/illuminate/config/Repository.php ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Config;
4
+
5
+ use ArrayAccess;
6
+ use Illuminate\Support\Arr;
7
+ use Illuminate\Contracts\Config\Repository as ConfigContract;
8
+
9
+ class Repository implements ArrayAccess, ConfigContract
10
+ {
11
+ /**
12
+ * All of the configuration items.
13
+ *
14
+ * @var array
15
+ */
16
+ protected $items = [];
17
+
18
+ /**
19
+ * Create a new configuration repository.
20
+ *
21
+ * @param array $items
22
+ * @return void
23
+ */
24
+ public function __construct(array $items = [])
25
+ {
26
+ $this->items = $items;
27
+ }
28
+
29
+ /**
30
+ * Determine if the given configuration value exists.
31
+ *
32
+ * @param string $key
33
+ * @return bool
34
+ */
35
+ public function has($key)
36
+ {
37
+ return Arr::has($this->items, $key);
38
+ }
39
+
40
+ /**
41
+ * Get the specified configuration value.
42
+ *
43
+ * @param string $key
44
+ * @param mixed $default
45
+ * @return mixed
46
+ */
47
+ public function get($key, $default = null)
48
+ {
49
+ return Arr::get($this->items, $key, $default);
50
+ }
51
+
52
+ /**
53
+ * Set a given configuration value.
54
+ *
55
+ * @param array|string $key
56
+ * @param mixed $value
57
+ * @return void
58
+ */
59
+ public function set($key, $value = null)
60
+ {
61
+ $keys = is_array($key) ? $key : [$key => $value];
62
+
63
+ foreach ($keys as $key => $value) {
64
+ Arr::set($this->items, $key, $value);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Prepend a value onto an array configuration value.
70
+ *
71
+ * @param string $key
72
+ * @param mixed $value
73
+ * @return void
74
+ */
75
+ public function prepend($key, $value)
76
+ {
77
+ $array = $this->get($key);
78
+
79
+ array_unshift($array, $value);
80
+
81
+ $this->set($key, $array);
82
+ }
83
+
84
+ /**
85
+ * Push a value onto an array configuration value.
86
+ *
87
+ * @param string $key
88
+ * @param mixed $value
89
+ * @return void
90
+ */
91
+ public function push($key, $value)
92
+ {
93
+ $array = $this->get($key);
94
+
95
+ $array[] = $value;
96
+
97
+ $this->set($key, $array);
98
+ }
99
+
100
+ /**
101
+ * Get all of the configuration items for the application.
102
+ *
103
+ * @return array
104
+ */
105
+ public function all()
106
+ {
107
+ return $this->items;
108
+ }
109
+
110
+ /**
111
+ * Determine if the given configuration option exists.
112
+ *
113
+ * @param string $key
114
+ * @return bool
115
+ */
116
+ public function offsetExists($key)
117
+ {
118
+ return $this->has($key);
119
+ }
120
+
121
+ /**
122
+ * Get a configuration option.
123
+ *
124
+ * @param string $key
125
+ * @return mixed
126
+ */
127
+ public function offsetGet($key)
128
+ {
129
+ return $this->get($key);
130
+ }
131
+
132
+ /**
133
+ * Set a configuration option.
134
+ *
135
+ * @param string $key
136
+ * @param mixed $value
137
+ * @return void
138
+ */
139
+ public function offsetSet($key, $value)
140
+ {
141
+ $this->set($key, $value);
142
+ }
143
+
144
+ /**
145
+ * Unset a configuration option.
146
+ *
147
+ * @param string $key
148
+ * @return void
149
+ */
150
+ public function offsetUnset($key)
151
+ {
152
+ $this->set($key, null);
153
+ }
154
+ }
vendor/illuminate/config/composer.json ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "illuminate/config",
3
+ "description": "The Illuminate Config package.",
4
+ "license": "MIT",
5
+ "homepage": "https://laravel.com",
6
+ "support": {
7
+ "issues": "https://github.com/laravel/framework/issues",
8
+ "source": "https://github.com/laravel/framework"
9
+ },
10
+ "authors": [
11
+ {
12
+ "name": "Taylor Otwell",
13
+ "email": "taylor@laravel.com"
14
+ }
15
+ ],
16
+ "require": {
17
+ "php": ">=5.6.4",
18
+ "illuminate/contracts": "5.4.*",
19
+ "illuminate/filesystem": "5.4.*",
20
+ "illuminate/support": "5.4.*"
21
+ },
22
+ "autoload": {
23
+ "psr-4": {
24
+ "Illuminate\\Config\\": ""
25
+ }
26
+ },
27
+ "extra": {
28
+ "branch-alias": {
29
+ "dev-master": "5.4-dev"
30
+ }
31
+ },
32
+ "config": {
33
+ "sort-packages": true
34
+ },
35
+ "minimum-stability": "dev"
36
+ }
vendor/illuminate/container/BoundMethod.php ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Container;
4
+
5
+ use Closure;
6
+ use ReflectionMethod;
7
+ use ReflectionFunction;
8
+ use InvalidArgumentException;
9
+
10
+ class BoundMethod
11
+ {
12
+ /**
13
+ * Call the given Closure / class@method and inject its dependencies.
14
+ *
15
+ * @param \Illuminate\Container\Container $container
16
+ * @param callable|string $callback
17
+ * @param array $parameters
18
+ * @param string|null $defaultMethod
19
+ * @return mixed
20
+ */
21
+ public static function call($container, $callback, array $parameters = [], $defaultMethod = null)
22
+ {
23
+ if (static::isCallableWithAtSign($callback) || $defaultMethod) {
24
+ return static::callClass($container, $callback, $parameters, $defaultMethod);
25
+ }
26
+
27
+ return static::callBoundMethod($container, $callback, function () use ($container, $callback, $parameters) {
28
+ return call_user_func_array(
29
+ $callback, static::getMethodDependencies($container, $callback, $parameters)
30
+ );
31
+ });
32
+ }
33
+
34
+ /**
35
+ * Call a string reference to a class using Class@method syntax.
36
+ *
37
+ * @param \Illuminate\Container\Container $container
38
+ * @param string $target
39
+ * @param array $parameters
40
+ * @param string|null $defaultMethod
41
+ * @return mixed
42
+ *
43
+ * @throws \InvalidArgumentException
44
+ */
45
+ protected static function callClass($container, $target, array $parameters = [], $defaultMethod = null)
46
+ {
47
+ $segments = explode('@', $target);
48
+
49
+ // We will assume an @ sign is used to delimit the class name from the method
50
+ // name. We will split on this @ sign and then build a callable array that
51
+ // we can pass right back into the "call" method for dependency binding.
52
+ $method = count($segments) == 2
53
+ ? $segments[1] : $defaultMethod;
54
+
55
+ if (is_null($method)) {
56
+ throw new InvalidArgumentException('Method not provided.');
57
+ }
58
+
59
+ return static::call(
60
+ $container, [$container->make($segments[0]), $method], $parameters
61
+ );
62
+ }
63
+
64
+ /**
65
+ * Call a method that has been bound to the container.
66
+ *
67
+ * @param \Illuminate\Container\Container $container
68
+ * @param callable $callback
69
+ * @param mixed $default
70
+ * @return mixed
71
+ */
72
+ protected static function callBoundMethod($container, $callback, $default)
73
+ {
74
+ if (! is_array($callback)) {
75
+ return $default instanceof Closure ? $default() : $default;
76
+ }
77
+
78
+ // Here we need to turn the array callable into a Class@method string we can use to
79
+ // examine the container and see if there are any method bindings for this given
80
+ // method. If there are, we can call this method binding callback immediately.
81
+ $method = static::normalizeMethod($callback);
82
+
83
+ if ($container->hasMethodBinding($method)) {
84
+ return $container->callMethodBinding($method, $callback[0]);
85
+ }
86
+
87
+ return $default instanceof Closure ? $default() : $default;
88
+ }
89
+
90
+ /**
91
+ * Normalize the given callback into a Class@method string.
92
+ *
93
+ * @param callable $callback
94
+ * @return string
95
+ */
96
+ protected static function normalizeMethod($callback)
97
+ {
98
+ $class = is_string($callback[0]) ? $callback[0] : get_class($callback[0]);
99
+
100
+ return "{$class}@{$callback[1]}";
101
+ }
102
+
103
+ /**
104
+ * Get all dependencies for a given method.
105
+ *
106
+ * @param \Illuminate\Container\Container
107
+ * @param callable|string $callback
108
+ * @param array $parameters
109
+ * @return array
110
+ */
111
+ protected static function getMethodDependencies($container, $callback, array $parameters = [])
112
+ {
113
+ $dependencies = [];
114
+
115
+ foreach (static::getCallReflector($callback)->getParameters() as $parameter) {
116
+ static::addDependencyForCallParameter($container, $parameter, $parameters, $dependencies);
117
+ }
118
+
119
+ return array_merge($dependencies, $parameters);
120
+ }
121
+
122
+ /**
123
+ * Get the proper reflection instance for the given callback.
124
+ *
125
+ * @param callable|string $callback
126
+ * @return \ReflectionFunctionAbstract
127
+ */
128
+ protected static function getCallReflector($callback)
129
+ {
130
+ if (is_string($callback) && strpos($callback, '::') !== false) {
131
+ $callback = explode('::', $callback);
132
+ }
133
+
134
+ return is_array($callback)
135
+ ? new ReflectionMethod($callback[0], $callback[1])
136
+ : new ReflectionFunction($callback);
137
+ }
138
+
139
+ /**
140
+ * Get the dependency for the given call parameter.
141
+ *
142
+ * @param \Illuminate\Container\Container $container
143
+ * @param \ReflectionParameter $parameter
144
+ * @param array $parameters
145
+ * @param array $dependencies
146
+ * @return mixed
147
+ */
148
+ protected static function addDependencyForCallParameter($container, $parameter,
149
+ array &$parameters, &$dependencies)
150
+ {
151
+ if (array_key_exists($parameter->name, $parameters)) {
152
+ $dependencies[] = $parameters[$parameter->name];
153
+
154
+ unset($parameters[$parameter->name]);
155
+ } elseif ($parameter->getClass()) {
156
+ $dependencies[] = $container->make($parameter->getClass()->name);
157
+ } elseif ($parameter->isDefaultValueAvailable()) {
158
+ $dependencies[] = $parameter->getDefaultValue();
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Determine if the given string is in Class@method syntax.
164
+ *
165
+ * @param mixed $callback
166
+ * @return bool
167
+ */
168
+ protected static function isCallableWithAtSign($callback)
169
+ {
170
+ return is_string($callback) && strpos($callback, '@') !== false;
171
+ }
172
+ }
vendor/illuminate/container/Container.php ADDED
@@ -0,0 +1,1222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Container;
4
+
5
+ use Closure;
6
+ use ArrayAccess;
7
+ use LogicException;
8
+ use ReflectionClass;
9
+ use ReflectionParameter;
10
+ use Illuminate\Contracts\Container\BindingResolutionException;
11
+ use Illuminate\Contracts\Container\Container as ContainerContract;
12
+
13
+ class Container implements ArrayAccess, ContainerContract
14
+ {
15
+ /**
16
+ * The current globally available container (if any).
17
+ *
18
+ * @var static
19
+ */
20
+ protected static $instance;
21
+
22
+ /**
23
+ * An array of the types that have been resolved.
24
+ *
25
+ * @var array
26
+ */
27
+ protected $resolved = [];
28
+
29
+ /**
30
+ * The container's bindings.
31
+ *
32
+ * @var array
33
+ */
34
+ protected $bindings = [];
35
+
36
+ /**
37
+ * The container's method bindings.
38
+ *
39
+ * @var array
40
+ */
41
+ protected $methodBindings = [];
42
+
43
+ /**
44
+ * The container's shared instances.
45
+ *
46
+ * @var array
47
+ */
48
+ protected $instances = [];
49
+
50
+ /**
51
+ * The registered type aliases.
52
+ *
53
+ * @var array
54
+ */
55
+ protected $aliases = [];
56
+
57
+ /**
58
+ * The registered aliases keyed by the abstract name.
59
+ *
60
+ * @var array
61
+ */
62
+ protected $abstractAliases = [];
63
+
64
+ /**
65
+ * The extension closures for services.
66
+ *
67
+ * @var array
68
+ */
69
+ protected $extenders = [];
70
+
71
+ /**
72
+ * All of the registered tags.
73
+ *
74
+ * @var array
75
+ */
76
+ protected $tags = [];
77
+
78
+ /**
79
+ * The stack of concretions currently being built.
80
+ *
81
+ * @var array
82
+ */
83
+ protected $buildStack = [];
84
+
85
+ /**
86
+ * The parameter override stack.
87
+ *
88
+ * @var array
89
+ */
90
+ protected $with = [];
91
+
92
+ /**
93
+ * The contextual binding map.
94
+ *
95
+ * @var array
96
+ */
97
+ public $contextual = [];
98
+
99
+ /**
100
+ * All of the registered rebound callbacks.
101
+ *
102
+ * @var array
103
+ */
104
+ protected $reboundCallbacks = [];
105
+
106
+ /**
107
+ * All of the global resolving callbacks.
108
+ *
109
+ * @var array
110
+ */
111
+ protected $globalResolvingCallbacks = [];
112
+
113
+ /**
114
+ * All of the global after resolving callbacks.
115
+ *
116
+ * @var array
117
+ */
118
+ protected $globalAfterResolvingCallbacks = [];
119
+
120
+ /**
121
+ * All of the resolving callbacks by class type.
122
+ *
123
+ * @var array
124
+ */
125
+ protected $resolvingCallbacks = [];
126
+
127
+ /**
128
+ * All of the after resolving callbacks by class type.
129
+ *
130
+ * @var array
131
+ */
132
+ protected $afterResolvingCallbacks = [];
133
+
134
+ /**
135
+ * Define a contextual binding.
136
+ *
137
+ * @param string $concrete
138
+ * @return \Illuminate\Contracts\Container\ContextualBindingBuilder
139
+ */
140
+ public function when($concrete)
141
+ {
142
+ return new ContextualBindingBuilder($this, $this->getAlias($concrete));
143
+ }
144
+
145
+ /**
146
+ * Determine if the given abstract type has been bound.
147
+ *
148
+ * @param string $abstract
149
+ * @return bool
150
+ */
151
+ public function bound($abstract)
152
+ {
153
+ return isset($this->bindings[$abstract]) ||
154
+ isset($this->instances[$abstract]) ||
155
+ $this->isAlias($abstract);
156
+ }
157
+
158
+ /**
159
+ * Determine if the given abstract type has been resolved.
160
+ *
161
+ * @param string $abstract
162
+ * @return bool
163
+ */
164
+ public function resolved($abstract)
165
+ {
166
+ if ($this->isAlias($abstract)) {
167
+ $abstract = $this->getAlias($abstract);
168
+ }
169
+
170
+ return isset($this->resolved[$abstract]) ||
171
+ isset($this->instances[$abstract]);
172
+ }
173
+
174
+ /**
175
+ * Determine if a given type is shared.
176
+ *
177
+ * @param string $abstract
178
+ * @return bool
179
+ */
180
+ public function isShared($abstract)
181
+ {
182
+ return isset($this->instances[$abstract]) ||
183
+ (isset($this->bindings[$abstract]['shared']) &&
184
+ $this->bindings[$abstract]['shared'] === true);
185
+ }
186
+
187
+ /**
188
+ * Determine if a given string is an alias.
189
+ *
190
+ * @param string $name
191
+ * @return bool
192
+ */
193
+ public function isAlias($name)
194
+ {
195
+ return isset($this->aliases[$name]);
196
+ }
197
+
198
+ /**
199
+ * Register a binding with the container.
200
+ *
201
+ * @param string|array $abstract
202
+ * @param \Closure|string|null $concrete
203
+ * @param bool $shared
204
+ * @return void
205
+ */
206
+ public function bind($abstract, $concrete = null, $shared = false)
207
+ {
208
+ // If no concrete type was given, we will simply set the concrete type to the
209
+ // abstract type. After that, the concrete type to be registered as shared
210
+ // without being forced to state their classes in both of the parameters.
211
+ $this->dropStaleInstances($abstract);
212
+
213
+ if (is_null($concrete)) {
214
+ $concrete = $abstract;
215
+ }
216
+
217
+ // If the factory is not a Closure, it means it is just a class name which is
218
+ // bound into this container to the abstract type and we will just wrap it
219
+ // up inside its own Closure to give us more convenience when extending.
220
+ if (! $concrete instanceof Closure) {
221
+ $concrete = $this->getClosure($abstract, $concrete);
222
+ }
223
+
224
+ $this->bindings[$abstract] = compact('concrete', 'shared');
225
+
226
+ // If the abstract type was already resolved in this container we'll fire the
227
+ // rebound listener so that any objects which have already gotten resolved
228
+ // can have their copy of the object updated via the listener callbacks.
229
+ if ($this->resolved($abstract)) {
230
+ $this->rebound($abstract);
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Get the Closure to be used when building a type.
236
+ *
237
+ * @param string $abstract
238
+ * @param string $concrete
239
+ * @return \Closure
240
+ */
241
+ protected function getClosure($abstract, $concrete)
242
+ {
243
+ return function ($container, $parameters = []) use ($abstract, $concrete) {
244
+ if ($abstract == $concrete) {
245
+ return $container->build($concrete);
246
+ }
247
+
248
+ return $container->makeWith($concrete, $parameters);
249
+ };
250
+ }
251
+
252
+ /**
253
+ * Determine if the container has a method binding.
254
+ *
255
+ * @param string $method
256
+ * @return bool
257
+ */
258
+ public function hasMethodBinding($method)
259
+ {
260
+ return isset($this->methodBindings[$method]);
261
+ }
262
+
263
+ /**
264
+ * Bind a callback to resolve with Container::call.
265
+ *
266
+ * @param string $method
267
+ * @param \Closure $callback
268
+ * @return void
269
+ */
270
+ public function bindMethod($method, $callback)
271
+ {
272
+ $this->methodBindings[$method] = $callback;
273
+ }
274
+
275
+ /**
276
+ * Get the method binding for the given method.
277
+ *
278
+ * @param string $method
279
+ * @param mixed $instance
280
+ * @return mixed
281
+ */
282
+ public function callMethodBinding($method, $instance)
283
+ {
284
+ return call_user_func($this->methodBindings[$method], $instance, $this);
285
+ }
286
+
287
+ /**
288
+ * Add a contextual binding to the container.
289
+ *
290
+ * @param string $concrete
291
+ * @param string $abstract
292
+ * @param \Closure|string $implementation
293
+ * @return void
294
+ */
295
+ public function addContextualBinding($concrete, $abstract, $implementation)
296
+ {
297
+ $this->contextual[$concrete][$this->getAlias($abstract)] = $implementation;
298
+ }
299
+
300
+ /**
301
+ * Register a binding if it hasn't already been registered.
302
+ *
303
+ * @param string $abstract
304
+ * @param \Closure|string|null $concrete
305
+ * @param bool $shared
306
+ * @return void
307
+ */
308
+ public function bindIf($abstract, $concrete = null, $shared = false)
309
+ {
310
+ if (! $this->bound($abstract)) {
311
+ $this->bind($abstract, $concrete, $shared);
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Register a shared binding in the container.
317
+ *
318
+ * @param string|array $abstract
319
+ * @param \Closure|string|null $concrete
320
+ * @return void
321
+ */
322
+ public function singleton($abstract, $concrete = null)
323
+ {
324
+ $this->bind($abstract, $concrete, true);
325
+ }
326
+
327
+ /**
328
+ * "Extend" an abstract type in the container.
329
+ *
330
+ * @param string $abstract
331
+ * @param \Closure $closure
332
+ * @return void
333
+ *
334
+ * @throws \InvalidArgumentException
335
+ */
336
+ public function extend($abstract, Closure $closure)
337
+ {
338
+ $abstract = $this->getAlias($abstract);
339
+
340
+ if (isset($this->instances[$abstract])) {
341
+ $this->instances[$abstract] = $closure($this->instances[$abstract], $this);
342
+
343
+ $this->rebound($abstract);
344
+ } else {
345
+ $this->extenders[$abstract][] = $closure;
346
+
347
+ if ($this->resolved($abstract)) {
348
+ $this->rebound($abstract);
349
+ }
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Register an existing instance as shared in the container.
355
+ *
356
+ * @param string $abstract
357
+ * @param mixed $instance
358
+ * @return void
359
+ */
360
+ public function instance($abstract, $instance)
361
+ {
362
+ $this->removeAbstractAlias($abstract);
363
+
364
+ $isBound = $this->bound($abstract);
365
+
366
+ unset($this->aliases[$abstract]);
367
+
368
+ // We'll check to determine if this type has been bound before, and if it has
369
+ // we will fire the rebound callbacks registered with the container and it
370
+ // can be updated with consuming classes that have gotten resolved here.
371
+ $this->instances[$abstract] = $instance;
372
+
373
+ if ($isBound) {
374
+ $this->rebound($abstract);
375
+ }
376
+ }
377
+
378
+ /**
379
+ * Remove an alias from the contextual binding alias cache.
380
+ *
381
+ * @param string $searched
382
+ * @return void
383
+ */
384
+ protected function removeAbstractAlias($searched)
385
+ {
386
+ if (! isset($this->aliases[$searched])) {
387
+ return;
388
+ }
389
+
390
+ foreach ($this->abstractAliases as $abstract => $aliases) {
391
+ foreach ($aliases as $index => $alias) {
392
+ if ($alias == $searched) {
393
+ unset($this->abstractAliases[$abstract][$index]);
394
+ }
395
+ }
396
+ }
397
+ }
398
+
399
+ /**
400
+ * Assign a set of tags to a given binding.
401
+ *
402
+ * @param array|string $abstracts
403
+ * @param array|mixed ...$tags
404
+ * @return void
405
+ */
406
+ public function tag($abstracts, $tags)
407
+ {
408
+ $tags = is_array($tags) ? $tags : array_slice(func_get_args(), 1);
409
+
410
+ foreach ($tags as $tag) {
411
+ if (! isset($this->tags[$tag])) {
412
+ $this->tags[$tag] = [];
413
+ }
414
+
415
+ foreach ((array) $abstracts as $abstract) {
416
+ $this->tags[$tag][] = $abstract;
417
+ }
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Resolve all of the bindings for a given tag.
423
+ *
424
+ * @param string $tag
425
+ * @return array
426
+ */
427
+ public function tagged($tag)
428
+ {
429
+ $results = [];
430
+
431
+ if (isset($this->tags[$tag])) {
432
+ foreach ($this->tags[$tag] as $abstract) {
433
+ $results[] = $this->make($abstract);
434
+ }
435
+ }
436
+
437
+ return $results;
438
+ }
439
+
440
+ /**
441
+ * Alias a type to a different name.
442
+ *
443
+ * @param string $abstract
444
+ * @param string $alias
445
+ * @return void
446
+ */
447
+ public function alias($abstract, $alias)
448
+ {
449
+ $this->aliases[$alias] = $abstract;
450
+
451
+ $this->abstractAliases[$abstract][] = $alias;
452
+ }
453
+
454
+ /**
455
+ * Bind a new callback to an abstract's rebind event.
456
+ *
457
+ * @param string $abstract
458
+ * @param \Closure $callback
459
+ * @return mixed
460
+ */
461
+ public function rebinding($abstract, Closure $callback)
462
+ {
463
+ $this->reboundCallbacks[$abstract = $this->getAlias($abstract)][] = $callback;
464
+
465
+ if ($this->bound($abstract)) {
466
+ return $this->make($abstract);
467
+ }
468
+ }
469
+
470
+ /**
471
+ * Refresh an instance on the given target and method.
472
+ *
473
+ * @param string $abstract
474
+ * @param mixed $target
475
+ * @param string $method
476
+ * @return mixed
477
+ */
478
+ public function refresh($abstract, $target, $method)
479
+ {
480
+ return $this->rebinding($abstract, function ($app, $instance) use ($target, $method) {
481
+ $target->{$method}($instance);
482
+ });
483
+ }
484
+
485
+ /**
486
+ * Fire the "rebound" callbacks for the given abstract type.
487
+ *
488
+ * @param string $abstract
489
+ * @return void
490
+ */
491
+ protected function rebound($abstract)
492
+ {
493
+ $instance = $this->make($abstract);
494
+
495
+ foreach ($this->getReboundCallbacks($abstract) as $callback) {
496
+ call_user_func($callback, $this, $instance);
497
+ }
498
+ }
499
+
500
+ /**
501
+ * Get the rebound callbacks for a given type.
502
+ *
503
+ * @param string $abstract
504
+ * @return array
505
+ */
506
+ protected function getReboundCallbacks($abstract)
507
+ {
508
+ if (isset($this->reboundCallbacks[$abstract])) {
509
+ return $this->reboundCallbacks[$abstract];
510
+ }
511
+
512
+ return [];
513
+ }
514
+
515
+ /**
516
+ * Wrap the given closure such that its dependencies will be injected when executed.
517
+ *
518
+ * @param \Closure $callback
519
+ * @param array $parameters
520
+ * @return \Closure
521
+ */
522
+ public function wrap(Closure $callback, array $parameters = [])
523
+ {
524
+ return function () use ($callback, $parameters) {
525
+ return $this->call($callback, $parameters);
526
+ };
527
+ }
528
+
529
+ /**
530
+ * Call the given Closure / class@method and inject its dependencies.
531
+ *
532
+ * @param callable|string $callback
533
+ * @param array $parameters
534
+ * @param string|null $defaultMethod
535
+ * @return mixed
536
+ */
537
+ public function call($callback, array $parameters = [], $defaultMethod = null)
538
+ {
539
+ return BoundMethod::call($this, $callback, $parameters, $defaultMethod);
540
+ }
541
+
542
+ /**
543
+ * Get a closure to resolve the given type from the container.
544
+ *
545
+ * @param string $abstract
546
+ * @return \Closure
547
+ */
548
+ public function factory($abstract)
549
+ {
550
+ return function () use ($abstract) {
551
+ return $this->make($abstract);
552
+ };
553
+ }
554
+
555
+ /**
556
+ * Resolve the given type with the given parameter overrides.
557
+ *
558
+ * @param string $abstract
559
+ * @param array $parameters
560
+ * @return mixed
561
+ */
562
+ public function makeWith($abstract, array $parameters)
563
+ {
564
+ return $this->resolve($abstract, $parameters);
565
+ }
566
+
567
+ /**
568
+ * Resolve the given type from the container.
569
+ *
570
+ * @param string $abstract
571
+ * @return mixed
572
+ */
573
+ public function make($abstract)
574
+ {
575
+ return $this->resolve($abstract);
576
+ }
577
+
578
+ /**
579
+ * Resolve the given type from the container.
580
+ *
581
+ * @param string $abstract
582
+ * @param array $parameters
583
+ * @return mixed
584
+ */
585
+ protected function resolve($abstract, $parameters = [])
586
+ {
587
+ $abstract = $this->getAlias($abstract);
588
+
589
+ $needsContextualBuild = ! empty($parameters) || ! is_null(
590
+ $this->getContextualConcrete($abstract)
591
+ );
592
+
593
+ // If an instance of the type is currently being managed as a singleton we'll
594
+ // just return an existing instance instead of instantiating new instances
595
+ // so the developer can keep using the same objects instance every time.
596
+ if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
597
+ return $this->instances[$abstract];
598
+ }
599
+
600
+ $this->with[] = $parameters;
601
+
602
+ $concrete = $this->getConcrete($abstract);
603
+
604
+ // We're ready to instantiate an instance of the concrete type registered for
605
+ // the binding. This will instantiate the types, as well as resolve any of
606
+ // its "nested" dependencies recursively until all have gotten resolved.
607
+ if ($this->isBuildable($concrete, $abstract)) {
608
+ $object = $this->build($concrete);
609
+ } else {
610
+ $object = $this->make($concrete);
611
+ }
612
+
613
+ // If we defined any extenders for this type, we'll need to spin through them
614
+ // and apply them to the object being built. This allows for the extension
615
+ // of services, such as changing configuration or decorating the object.
616
+ foreach ($this->getExtenders($abstract) as $extender) {
617
+ $object = $extender($object, $this);
618
+ }
619
+
620
+ // If the requested type is registered as a singleton we'll want to cache off
621
+ // the instances in "memory" so we can return it later without creating an
622
+ // entirely new instance of an object on each subsequent request for it.
623
+ if ($this->isShared($abstract) && ! $needsContextualBuild) {
624
+ $this->instances[$abstract] = $object;
625
+ }
626
+
627
+ $this->fireResolvingCallbacks($abstract, $object);
628
+
629
+ // Before returning, we will also set the resolved flag to "true" and pop off
630
+ // the parameter overrides for this build. After those two things are done
631
+ // we will be ready to return back the fully constructed class instance.
632
+ $this->resolved[$abstract] = true;
633
+
634
+ array_pop($this->with);
635
+
636
+ return $object;
637
+ }
638
+
639
+ /**
640
+ * Get the concrete type for a given abstract.
641
+ *
642
+ * @param string $abstract
643
+ * @return mixed $concrete
644
+ */
645
+ protected function getConcrete($abstract)
646
+ {
647
+ if (! is_null($concrete = $this->getContextualConcrete($abstract))) {
648
+ return $concrete;
649
+ }
650
+
651
+ // If we don't have a registered resolver or concrete for the type, we'll just
652
+ // assume each type is a concrete name and will attempt to resolve it as is
653
+ // since the container should be able to resolve concretes automatically.
654
+ if (isset($this->bindings[$abstract])) {
655
+ return $this->bindings[$abstract]['concrete'];
656
+ }
657
+
658
+ return $abstract;
659
+ }
660
+
661
+ /**
662
+ * Get the contextual concrete binding for the given abstract.
663
+ *
664
+ * @param string $abstract
665
+ * @return string|null
666
+ */
667
+ protected function getContextualConcrete($abstract)
668
+ {
669
+ if (! is_null($binding = $this->findInContextualBindings($abstract))) {
670
+ return $binding;
671
+ }
672
+
673
+ // Next we need to see if a contextual binding might be bound under an alias of the
674
+ // given abstract type. So, we will need to check if any aliases exist with this
675
+ // type and then spin through them and check for contextual bindings on these.
676
+ if (empty($this->abstractAliases[$abstract])) {
677
+ return;
678
+ }
679
+
680
+ foreach ($this->abstractAliases[$abstract] as $alias) {
681
+ if (! is_null($binding = $this->findInContextualBindings($alias))) {
682
+ return $binding;
683
+ }
684
+ }
685
+ }
686
+
687
+ /**
688
+ * Find the concrete binding for the given abstract in the contextual binding array.
689
+ *
690
+ * @param string $abstract
691
+ * @return string|null
692
+ */
693
+ protected function findInContextualBindings($abstract)
694
+ {
695
+ if (isset($this->contextual[end($this->buildStack)][$abstract])) {
696
+ return $this->contextual[end($this->buildStack)][$abstract];
697
+ }
698
+ }
699
+
700
+ /**
701
+ * Determine if the given concrete is buildable.
702
+ *
703
+ * @param mixed $concrete
704
+ * @param string $abstract
705
+ * @return bool
706
+ */
707
+ protected function isBuildable($concrete, $abstract)
708
+ {
709
+ return $concrete === $abstract || $concrete instanceof Closure;
710
+ }
711
+
712
+ /**
713
+ * Instantiate a concrete instance of the given type.
714
+ *
715
+ * @param string $concrete
716
+ * @return mixed
717
+ *
718
+ * @throws \Illuminate\Contracts\Container\BindingResolutionException
719
+ */
720
+ public function build($concrete)
721
+ {
722
+ // If the concrete type is actually a Closure, we will just execute it and
723
+ // hand back the results of the functions, which allows functions to be
724
+ // used as resolvers for more fine-tuned resolution of these objects.
725
+ if ($concrete instanceof Closure) {
726
+ return $concrete($this, $this->getLastParameterOverride());
727
+ }
728
+
729
+ $reflector = new ReflectionClass($concrete);
730
+
731
+ // If the type is not instantiable, the developer is attempting to resolve
732
+ // an abstract type such as an Interface of Abstract Class and there is
733
+ // no binding registered for the abstractions so we need to bail out.
734
+ if (! $reflector->isInstantiable()) {
735
+ return $this->notInstantiable($concrete);
736
+ }
737
+
738
+ $this->buildStack[] = $concrete;
739
+
740
+ $constructor = $reflector->getConstructor();
741
+
742
+ // If there are no constructors, that means there are no dependencies then
743
+ // we can just resolve the instances of the objects right away, without
744
+ // resolving any other types or dependencies out of these containers.
745
+ if (is_null($constructor)) {
746
+ array_pop($this->buildStack);
747
+
748
+ return new $concrete;
749
+ }
750
+
751
+ $dependencies = $constructor->getParameters();
752
+
753
+ // Once we have all the constructor's parameters we can create each of the
754
+ // dependency instances and then use the reflection instances to make a
755
+ // new instance of this class, injecting the created dependencies in.
756
+ $instances = $this->resolveDependencies(
757
+ $dependencies
758
+ );
759
+
760
+ array_pop($this->buildStack);
761
+
762
+ return $reflector->newInstanceArgs($instances);
763
+ }
764
+
765
+ /**
766
+ * Resolve all of the dependencies from the ReflectionParameters.
767
+ *
768
+ * @param array $dependencies
769
+ * @return array
770
+ */
771
+ protected function resolveDependencies(array $dependencies)
772
+ {
773
+ $results = [];
774
+
775
+ foreach ($dependencies as $dependency) {
776
+ // If this dependency has a override for this particular build we will use
777
+ // that instead as the value. Otherwise, we will continue with this run
778
+ // of resolutions and let reflection attempt to determine the result.
779
+ if ($this->hasParameterOverride($dependency)) {
780
+ $results[] = $this->getParameterOverride($dependency);
781
+
782
+ continue;
783
+ }
784
+
785
+ // If the class is null, it means the dependency is a string or some other
786
+ // primitive type which we can not resolve since it is not a class and
787
+ // we will just bomb out with an error since we have no-where to go.
788
+ $results[] = is_null($class = $dependency->getClass())
789
+ ? $this->resolvePrimitive($dependency)
790
+ : $this->resolveClass($dependency);
791
+ }
792
+
793
+ return $results;
794
+ }
795
+
796
+ /**
797
+ * Determine if the given dependency has a parameter override from makeWith.
798
+ *
799
+ * @param \ReflectionParameter $dependency
800
+ * @return bool
801
+ */
802
+ protected function hasParameterOverride($dependency)
803
+ {
804
+ return array_key_exists(
805
+ $dependency->name, $this->getLastParameterOverride()
806
+ );
807
+ }
808
+
809
+ /**
810
+ * Get a parameter override for a dependency.
811
+ *
812
+ * @param \ReflectionParameter $dependency
813
+ * @return mixed
814
+ */
815
+ protected function getParameterOverride($dependency)
816
+ {
817
+ return $this->getLastParameterOverride()[$dependency->name];
818
+ }
819
+
820
+ /**
821
+ * Get the last parameter override.
822
+ *
823
+ * @return array
824
+ */
825
+ protected function getLastParameterOverride()
826
+ {
827
+ return count($this->with) ? end($this->with) : [];
828
+ }
829
+
830
+ /**
831
+ * Resolve a non-class hinted primitive dependency.
832
+ *
833
+ * @param \ReflectionParameter $parameter
834
+ * @return mixed
835
+ *
836
+ * @throws \Illuminate\Contracts\Container\BindingResolutionException
837
+ */
838
+ protected function resolvePrimitive(ReflectionParameter $parameter)
839
+ {
840
+ if (! is_null($concrete = $this->getContextualConcrete('$'.$parameter->name))) {
841
+ return $concrete instanceof Closure ? $concrete($this) : $concrete;
842
+ }
843
+
844
+ if ($parameter->isDefaultValueAvailable()) {
845
+ return $parameter->getDefaultValue();
846
+ }
847
+
848
+ $this->unresolvablePrimitive($parameter);
849
+ }
850
+
851
+ /**
852
+ * Resolve a class based dependency from the container.
853
+ *
854
+ * @param \ReflectionParameter $parameter
855
+ * @return mixed
856
+ *
857
+ * @throws \Illuminate\Contracts\Container\BindingResolutionException
858
+ */
859
+ protected function resolveClass(ReflectionParameter $parameter)
860
+ {
861
+ try {
862
+ return $this->make($parameter->getClass()->name);
863
+ }
864
+
865
+ // If we can not resolve the class instance, we will check to see if the value
866
+ // is optional, and if it is we will return the optional parameter value as
867
+ // the value of the dependency, similarly to how we do this with scalars.
868
+ catch (BindingResolutionException $e) {
869
+ if ($parameter->isOptional()) {
870
+ return $parameter->getDefaultValue();
871
+ }
872
+
873
+ throw $e;
874
+ }
875
+ }
876
+
877
+ /**
878
+ * Throw an exception that the concrete is not instantiable.
879
+ *
880
+ * @param string $concrete
881
+ * @return void
882
+ *
883
+ * @throws \Illuminate\Contracts\Container\BindingResolutionException
884
+ */
885
+ protected function notInstantiable($concrete)
886
+ {
887
+ if (! empty($this->buildStack)) {
888
+ $previous = implode(', ', $this->buildStack);
889
+
890
+ $message = "Target [$concrete] is not instantiable while building [$previous].";
891
+ } else {
892
+ $message = "Target [$concrete] is not instantiable.";
893
+ }
894
+
895
+ throw new BindingResolutionException($message);
896
+ }
897
+
898
+ /**
899
+ * Throw an exception for an unresolvable primitive.
900
+ *
901
+ * @param \ReflectionParameter $parameter
902
+ * @return void
903
+ *
904
+ * @throws \Illuminate\Contracts\Container\BindingResolutionException
905
+ */
906
+ protected function unresolvablePrimitive(ReflectionParameter $parameter)
907
+ {
908
+ $message = "Unresolvable dependency resolving [$parameter] in class {$parameter->getDeclaringClass()->getName()}";
909
+
910
+ throw new BindingResolutionException($message);
911
+ }
912
+
913
+ /**
914
+ * Register a new resolving callback.
915
+ *
916
+ * @param string $abstract
917
+ * @param \Closure|null $callback
918
+ * @return void
919
+ */
920
+ public function resolving($abstract, Closure $callback = null)
921
+ {
922
+ if (is_string($abstract)) {
923
+ $abstract = $this->getAlias($abstract);
924
+ }
925
+
926
+ if (is_null($callback) && $abstract instanceof Closure) {
927
+ $this->globalResolvingCallbacks[] = $abstract;
928
+ } else {
929
+ $this->resolvingCallbacks[$abstract][] = $callback;
930
+ }
931
+ }
932
+
933
+ /**
934
+ * Register a new after resolving callback for all types.
935
+ *
936
+ * @param string $abstract
937
+ * @param \Closure|null $callback
938
+ * @return void
939
+ */
940
+ public function afterResolving($abstract, Closure $callback = null)
941
+ {
942
+ if (is_string($abstract)) {
943
+ $abstract = $this->getAlias($abstract);
944
+ }
945
+
946
+ if ($abstract instanceof Closure && is_null($callback)) {
947
+ $this->globalAfterResolvingCallbacks[] = $abstract;
948
+ } else {
949
+ $this->afterResolvingCallbacks[$abstract][] = $callback;
950
+ }
951
+ }
952
+
953
+ /**
954
+ * Fire all of the resolving callbacks.
955
+ *
956
+ * @param string $abstract
957
+ * @param mixed $object
958
+ * @return void
959
+ */
960
+ protected function fireResolvingCallbacks($abstract, $object)
961
+ {
962
+ $this->fireCallbackArray($object, $this->globalResolvingCallbacks);
963
+
964
+ $this->fireCallbackArray(
965
+ $object, $this->getCallbacksForType($abstract, $object, $this->resolvingCallbacks)
966
+ );
967
+
968
+ $this->fireAfterResolvingCallbacks($abstract, $object);
969
+ }
970
+
971
+ /**
972
+ * Fire all of the after resolving callbacks.
973
+ *
974
+ * @param string $abstract
975
+ * @param mixed $object
976
+ * @return void
977
+ */
978
+ protected function fireAfterResolvingCallbacks($abstract, $object)
979
+ {
980
+ $this->fireCallbackArray($object, $this->globalAfterResolvingCallbacks);
981
+
982
+ $this->fireCallbackArray(
983
+ $object, $this->getCallbacksForType($abstract, $object, $this->afterResolvingCallbacks)
984
+ );
985
+ }
986
+
987
+ /**
988
+ * Get all callbacks for a given type.
989
+ *
990
+ * @param string $abstract
991
+ * @param object $object
992
+ * @param array $callbacksPerType
993
+ *
994
+ * @return array
995
+ */
996
+ protected function getCallbacksForType($abstract, $object, array $callbacksPerType)
997
+ {
998
+ $results = [];
999
+
1000
+ foreach ($callbacksPerType as $type => $callbacks) {
1001
+ if ($type === $abstract || $object instanceof $type) {
1002
+ $results = array_merge($results, $callbacks);
1003
+ }
1004
+ }
1005
+
1006
+ return $results;
1007
+ }
1008
+
1009
+ /**
1010
+ * Fire an array of callbacks with an object.
1011
+ *
1012
+ * @param mixed $object
1013
+ * @param array $callbacks
1014
+ * @return void
1015
+ */
1016
+ protected function fireCallbackArray($object, array $callbacks)
1017
+ {
1018
+ foreach ($callbacks as $callback) {
1019
+ $callback($object, $this);
1020
+ }
1021
+ }
1022
+
1023
+ /**
1024
+ * Get the container's bindings.
1025
+ *
1026
+ * @return array
1027
+ */
1028
+ public function getBindings()
1029
+ {
1030
+ return $this->bindings;
1031
+ }
1032
+
1033
+ /**
1034
+ * Get the alias for an abstract if available.
1035
+ *
1036
+ * @param string $abstract
1037
+ * @return string
1038
+ *
1039
+ * @throws \LogicException
1040
+ */
1041
+ public function getAlias($abstract)
1042
+ {
1043
+ if (! isset($this->aliases[$abstract])) {
1044
+ return $abstract;
1045
+ }
1046
+
1047
+ if ($this->aliases[$abstract] === $abstract) {
1048
+ throw new LogicException("[{$abstract}] is aliased to itself.");
1049
+ }
1050
+
1051
+ return $this->getAlias($this->aliases[$abstract]);
1052
+ }
1053
+
1054
+ /**
1055
+ * Get the extender callbacks for a given type.
1056
+ *
1057
+ * @param string $abstract
1058
+ * @return array
1059
+ */
1060
+ protected function getExtenders($abstract)
1061
+ {
1062
+ $abstract = $this->getAlias($abstract);
1063
+
1064
+ if (isset($this->extenders[$abstract])) {
1065
+ return $this->extenders[$abstract];
1066
+ }
1067
+
1068
+ return [];
1069
+ }
1070
+
1071
+ /**
1072
+ * Remove all of the extender callbacks for a given type.
1073
+ *
1074
+ * @param string $abstract
1075
+ * @return void
1076
+ */
1077
+ public function forgetExtenders($abstract)
1078
+ {
1079
+ unset($this->extenders[$this->getAlias($abstract)]);
1080
+ }
1081
+
1082
+ /**
1083
+ * Drop all of the stale instances and aliases.
1084
+ *
1085
+ * @param string $abstract
1086
+ * @return void
1087
+ */
1088
+ protected function dropStaleInstances($abstract)
1089
+ {
1090
+ unset($this->instances[$abstract], $this->aliases[$abstract]);
1091
+ }
1092
+
1093
+ /**
1094
+ * Remove a resolved instance from the instance cache.
1095
+ *
1096
+ * @param string $abstract
1097
+ * @return void
1098
+ */
1099
+ public function forgetInstance($abstract)
1100
+ {
1101
+ unset($this->instances[$abstract]);
1102
+ }
1103
+
1104
+ /**
1105
+ * Clear all of the instances from the container.
1106
+ *
1107
+ * @return void
1108
+ */
1109
+ public function forgetInstances()
1110
+ {
1111
+ $this->instances = [];
1112
+ }
1113
+
1114
+ /**
1115
+ * Flush the container of all bindings and resolved instances.
1116
+ *
1117
+ * @return void
1118
+ */
1119
+ public function flush()
1120
+ {
1121
+ $this->aliases = [];
1122
+ $this->resolved = [];
1123
+ $this->bindings = [];
1124
+ $this->instances = [];
1125
+ $this->abstractAliases = [];
1126
+ }
1127
+
1128
+ /**
1129
+ * Set the globally available instance of the container.
1130
+ *
1131
+ * @return static
1132
+ */
1133
+ public static function getInstance()
1134
+ {
1135
+ if (is_null(static::$instance)) {
1136
+ static::$instance = new static;
1137
+ }
1138
+
1139
+ return static::$instance;
1140
+ }
1141
+
1142
+ /**
1143
+ * Set the shared instance of the container.
1144
+ *
1145
+ * @param \Illuminate\Contracts\Container\Container|null $container
1146
+ * @return static
1147
+ */
1148
+ public static function setInstance(ContainerContract $container = null)
1149
+ {
1150
+ return static::$instance = $container;
1151
+ }
1152
+
1153
+ /**
1154
+ * Determine if a given offset exists.
1155
+ *
1156
+ * @param string $key
1157
+ * @return bool
1158
+ */
1159
+ public function offsetExists($key)
1160
+ {
1161
+ return $this->bound($key);
1162
+ }
1163
+
1164
+ /**
1165
+ * Get the value at a given offset.
1166
+ *
1167
+ * @param string $key
1168
+ * @return mixed
1169
+ */
1170
+ public function offsetGet($key)
1171
+ {
1172
+ return $this->make($key);
1173
+ }
1174
+
1175
+ /**
1176
+ * Set the value at a given offset.
1177
+ *
1178
+ * @param string $key
1179
+ * @param mixed $value
1180
+ * @return void
1181
+ */
1182
+ public function offsetSet($key, $value)
1183
+ {
1184
+ $this->bind($key, $value instanceof Closure ? $value : function () use ($value) {
1185
+ return $value;
1186
+ });
1187
+ }
1188
+
1189
+ /**
1190
+ * Unset the value at a given offset.
1191
+ *
1192
+ * @param string $key
1193
+ * @return void
1194
+ */
1195
+ public function offsetUnset($key)
1196
+ {
1197
+ unset($this->bindings[$key], $this->instances[$key], $this->resolved[$key]);
1198
+ }
1199
+
1200
+ /**
1201
+ * Dynamically access container services.
1202
+ *
1203
+ * @param string $key
1204
+ * @return mixed
1205
+ */
1206
+ public function __get($key)
1207
+ {
1208
+ return $this[$key];
1209
+ }
1210
+
1211
+ /**
1212
+ * Dynamically set container services.
1213
+ *
1214
+ * @param string $key
1215
+ * @param mixed $value
1216
+ * @return void
1217
+ */
1218
+ public function __set($key, $value)
1219
+ {
1220
+ $this[$key] = $value;
1221
+ }
1222
+ }
vendor/illuminate/container/ContextualBindingBuilder.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Container;
4
+
5
+ use Illuminate\Contracts\Container\ContextualBindingBuilder as ContextualBindingBuilderContract;
6
+
7
+ class ContextualBindingBuilder implements ContextualBindingBuilderContract
8
+ {
9
+ /**
10
+ * The underlying container instance.
11
+ *
12
+ * @var \Illuminate\Container\Container
13
+ */
14
+ protected $container;
15
+
16
+ /**
17
+ * The concrete instance.
18
+ *
19
+ * @var string
20
+ */
21
+ protected $concrete;
22
+
23
+ /**
24
+ * The abstract target.
25
+ *
26
+ * @var string
27
+ */
28
+ protected $needs;
29
+
30
+ /**
31
+ * Create a new contextual binding builder.
32
+ *
33
+ * @param \Illuminate\Container\Container $container
34
+ * @param string $concrete
35
+ * @return void
36
+ */
37
+ public function __construct(Container $container, $concrete)
38
+ {
39
+ $this->concrete = $concrete;
40
+ $this->container = $container;
41
+ }
42
+
43
+ /**
44
+ * Define the abstract target that depends on the context.
45
+ *
46
+ * @param string $abstract
47
+ * @return $this
48
+ */
49
+ public function needs($abstract)
50
+ {
51
+ $this->needs = $abstract;
52
+
53
+ return $this;
54
+ }
55
+
56
+ /**
57
+ * Define the implementation for the contextual binding.
58
+ *
59
+ * @param \Closure|string $implementation
60
+ * @return void
61
+ */
62
+ public function give($implementation)
63
+ {
64
+ $this->container->addContextualBinding(
65
+ $this->concrete, $this->needs, $implementation
66
+ );
67
+ }
68
+ }
vendor/illuminate/container/composer.json ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "illuminate/container",
3
+ "description": "The Illuminate Container package.",
4
+ "license": "MIT",
5
+ "homepage": "https://laravel.com",
6
+ "support": {
7
+ "issues": "https://github.com/laravel/framework/issues",
8
+ "source": "https://github.com/laravel/framework"
9
+ },
10
+ "authors": [
11
+ {
12
+ "name": "Taylor Otwell",
13
+ "email": "taylor@laravel.com"
14
+ }
15
+ ],
16
+ "require": {
17
+ "php": ">=5.6.4",
18
+ "illuminate/contracts": "5.4.*"
19
+ },
20
+ "autoload": {
21
+ "psr-4": {
22
+ "Illuminate\\Container\\": ""
23
+ }
24
+ },
25
+ "extra": {
26
+ "branch-alias": {
27
+ "dev-master": "5.4-dev"
28
+ }
29
+ },
30
+ "config": {
31
+ "sort-packages": true
32
+ },
33
+ "minimum-stability": "dev"
34
+ }
vendor/illuminate/contracts/Auth/Access/Authorizable.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Auth\Access;
4
+
5
+ interface Authorizable
6
+ {
7
+ /**
8
+ * Determine if the entity has a given ability.
9
+ *
10
+ * @param string $ability
11
+ * @param array|mixed $arguments
12
+ * @return bool
13
+ */
14
+ public function can($ability, $arguments = []);
15
+ }
vendor/illuminate/contracts/Auth/Access/Gate.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Auth\Access;
4
+
5
+ interface Gate
6
+ {
7
+ /**
8
+ * Determine if a given ability has been defined.
9
+ *
10
+ * @param string $ability
11
+ * @return bool
12
+ */
13
+ public function has($ability);
14
+
15
+ /**
16
+ * Define a new ability.
17
+ *
18
+ * @param string $ability
19
+ * @param callable|string $callback
20
+ * @return $this
21
+ */
22
+ public function define($ability, $callback);
23
+
24
+ /**
25
+ * Define a policy class for a given class type.
26
+ *
27
+ * @param string $class
28
+ * @param string $policy
29
+ * @return $this
30
+ */
31
+ public function policy($class, $policy);
32
+
33
+ /**
34
+ * Register a callback to run before all Gate checks.
35
+ *
36
+ * @param callable $callback
37
+ * @return $this
38
+ */
39
+ public function before(callable $callback);
40
+
41
+ /**
42
+ * Register a callback to run after all Gate checks.
43
+ *
44
+ * @param callable $callback
45
+ * @return $this
46
+ */
47
+ public function after(callable $callback);
48
+
49
+ /**
50
+ * Determine if the given ability should be granted for the current user.
51
+ *
52
+ * @param string $ability
53
+ * @param array|mixed $arguments
54
+ * @return bool
55
+ */
56
+ public function allows($ability, $arguments = []);
57
+
58
+ /**
59
+ * Determine if the given ability should be denied for the current user.
60
+ *
61
+ * @param string $ability
62
+ * @param array|mixed $arguments
63
+ * @return bool
64
+ */
65
+ public function denies($ability, $arguments = []);
66
+
67
+ /**
68
+ * Determine if the given ability should be granted.
69
+ *
70
+ * @param string $ability
71
+ * @param array|mixed $arguments
72
+ * @return bool
73
+ */
74
+ public function check($ability, $arguments = []);
75
+
76
+ /**
77
+ * Determine if the given ability should be granted for the current user.
78
+ *
79
+ * @param string $ability
80
+ * @param array|mixed $arguments
81
+ * @return \Illuminate\Auth\Access\Response
82
+ *
83
+ * @throws \Illuminate\Auth\Access\AuthorizationException
84
+ */
85
+ public function authorize($ability, $arguments = []);
86
+
87
+ /**
88
+ * Get a policy instance for a given class.
89
+ *
90
+ * @param object|string $class
91
+ * @return mixed
92
+ *
93
+ * @throws \InvalidArgumentException
94
+ */
95
+ public function getPolicyFor($class);
96
+
97
+ /**
98
+ * Get a guard instance for the given user.
99
+ *
100
+ * @param \Illuminate\Contracts\Auth\Authenticatable|mixed $user
101
+ * @return static
102
+ */
103
+ public function forUser($user);
104
+ }
vendor/illuminate/contracts/Auth/Authenticatable.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Auth;
4
+
5
+ interface Authenticatable
6
+ {
7
+ /**
8
+ * Get the name of the unique identifier for the user.
9
+ *
10
+ * @return string
11
+ */
12
+ public function getAuthIdentifierName();
13
+
14
+ /**
15
+ * Get the unique identifier for the user.
16
+ *
17
+ * @return mixed
18
+ */
19
+ public function getAuthIdentifier();
20
+
21
+ /**
22
+ * Get the password for the user.
23
+ *
24
+ * @return string
25
+ */
26
+ public function getAuthPassword();
27
+
28
+ /**
29
+ * Get the token value for the "remember me" session.
30
+ *
31
+ * @return string
32
+ */
33
+ public function getRememberToken();
34
+
35
+ /**
36
+ * Set the token value for the "remember me" session.
37
+ *
38
+ * @param string $value
39
+ * @return void
40
+ */
41
+ public function setRememberToken($value);
42
+
43
+ /**
44
+ * Get the column name for the "remember me" token.
45
+ *
46
+ * @return string
47
+ */
48
+ public function getRememberTokenName();
49
+ }
vendor/illuminate/contracts/Auth/CanResetPassword.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Auth;
4
+
5
+ interface CanResetPassword
6
+ {
7
+ /**
8
+ * Get the e-mail address where password reset links are sent.
9
+ *
10
+ * @return string
11
+ */
12
+ public function getEmailForPasswordReset();
13
+
14
+ /**
15
+ * Send the password reset notification.
16
+ *
17
+ * @param string $token
18
+ * @return void
19
+ */
20
+ public function sendPasswordResetNotification($token);
21
+ }
vendor/illuminate/contracts/Auth/Factory.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Auth;
4
+
5
+ interface Factory
6
+ {
7
+ /**
8
+ * Get a guard instance by name.
9
+ *
10
+ * @param string|null $name
11
+ * @return mixed
12
+ */
13
+ public function guard($name = null);
14
+
15
+ /**
16
+ * Set the default guard the factory should serve.
17
+ *
18
+ * @param string $name
19
+ * @return void
20
+ */
21
+ public function shouldUse($name);
22
+ }
vendor/illuminate/contracts/Auth/Guard.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Auth;
4
+
5
+ interface Guard
6
+ {
7
+ /**
8
+ * Determine if the current user is authenticated.
9
+ *
10
+ * @return bool
11
+ */
12
+ public function check();
13
+
14
+ /**
15
+ * Determine if the current user is a guest.
16
+ *
17
+ * @return bool
18
+ */
19
+ public function guest();
20
+
21
+ /**
22
+ * Get the currently authenticated user.
23
+ *
24
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
25
+ */
26
+ public function user();
27
+
28
+ /**
29
+ * Get the ID for the currently authenticated user.
30
+ *
31
+ * @return int|null
32
+ */
33
+ public function id();
34
+
35
+ /**
36
+ * Validate a user's credentials.
37
+ *
38
+ * @param array $credentials
39
+ * @return bool
40
+ */
41
+ public function validate(array $credentials = []);
42
+
43
+ /**
44
+ * Set the current user.
45
+ *
46
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
47
+ * @return void
48
+ */
49
+ public function setUser(Authenticatable $user);
50
+ }
vendor/illuminate/contracts/Auth/PasswordBroker.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Auth;
4
+
5
+ use Closure;
6
+
7
+ interface PasswordBroker
8
+ {
9
+ /**
10
+ * Constant representing a successfully sent reminder.
11
+ *
12
+ * @var string
13
+ */
14
+ const RESET_LINK_SENT = 'passwords.sent';
15
+
16
+ /**
17
+ * Constant representing a successfully reset password.
18
+ *
19
+ * @var string
20
+ */
21
+ const PASSWORD_RESET = 'passwords.reset';
22
+
23
+ /**
24
+ * Constant representing the user not found response.
25
+ *
26
+ * @var string
27
+ */
28
+ const INVALID_USER = 'passwords.user';
29
+
30
+ /**
31
+ * Constant representing an invalid password.
32
+ *
33
+ * @var string
34
+ */
35
+ const INVALID_PASSWORD = 'passwords.password';
36
+
37
+ /**
38
+ * Constant representing an invalid token.
39
+ *
40
+ * @var string
41
+ */
42
+ const INVALID_TOKEN = 'passwords.token';
43
+
44
+ /**
45
+ * Send a password reset link to a user.
46
+ *
47
+ * @param array $credentials
48
+ * @return string
49
+ */
50
+ public function sendResetLink(array $credentials);
51
+
52
+ /**
53
+ * Reset the password for the given token.
54
+ *
55
+ * @param array $credentials
56
+ * @param \Closure $callback
57
+ * @return mixed
58
+ */
59
+ public function reset(array $credentials, Closure $callback);
60
+
61
+ /**
62
+ * Set a custom password validator.
63
+ *
64
+ * @param \Closure $callback
65
+ * @return void
66
+ */
67
+ public function validator(Closure $callback);
68
+
69
+ /**
70
+ * Determine if the passwords match for the request.
71
+ *
72
+ * @param array $credentials
73
+ * @return bool
74
+ */
75
+ public function validateNewPassword(array $credentials);
76
+ }
vendor/illuminate/contracts/Auth/PasswordBrokerFactory.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Auth;
4
+
5
+ interface PasswordBrokerFactory
6
+ {
7
+ /**
8
+ * Get a password broker instance by name.
9
+ *
10
+ * @param string|null $name
11
+ * @return mixed
12
+ */
13
+ public function broker($name = null);
14
+ }
vendor/illuminate/contracts/Auth/StatefulGuard.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Auth;
4
+
5
+ interface StatefulGuard extends Guard
6
+ {
7
+ /**
8
+ * Attempt to authenticate a user using the given credentials.
9
+ *
10
+ * @param array $credentials
11
+ * @param bool $remember
12
+ * @return bool
13
+ */
14
+ public function attempt(array $credentials = [], $remember = false);
15
+
16
+ /**
17
+ * Log a user into the application without sessions or cookies.
18
+ *
19
+ * @param array $credentials
20
+ * @return bool
21
+ */
22
+ public function once(array $credentials = []);
23
+
24
+ /**
25
+ * Log a user into the application.
26
+ *
27
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
28
+ * @param bool $remember
29
+ * @return void
30
+ */
31
+ public function login(Authenticatable $user, $remember = false);
32
+
33
+ /**
34
+ * Log the given user ID into the application.
35
+ *
36
+ * @param mixed $id
37
+ * @param bool $remember
38
+ * @return \Illuminate\Contracts\Auth\Authenticatable
39
+ */
40
+ public function loginUsingId($id, $remember = false);
41
+
42
+ /**
43
+ * Log the given user ID into the application without sessions or cookies.
44
+ *
45
+ * @param mixed $id
46
+ * @return bool
47
+ */
48
+ public function onceUsingId($id);
49
+
50
+ /**
51
+ * Determine if the user was authenticated via "remember me" cookie.
52
+ *
53
+ * @return bool
54
+ */
55
+ public function viaRemember();
56
+
57
+ /**
58
+ * Log the user out of the application.
59
+ *
60
+ * @return void
61
+ */
62
+ public function logout();
63
+ }
vendor/illuminate/contracts/Auth/SupportsBasicAuth.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Auth;
4
+
5
+ interface SupportsBasicAuth
6
+ {
7
+ /**
8
+ * Attempt to authenticate using HTTP Basic Auth.
9
+ *
10
+ * @param string $field
11
+ * @param array $extraConditions
12
+ * @return \Symfony\Component\HttpFoundation\Response|null
13
+ */
14
+ public function basic($field = 'email', $extraConditions = []);
15
+
16
+ /**
17
+ * Perform a stateless HTTP Basic login attempt.
18
+ *
19
+ * @param string $field
20
+ * @param array $extraConditions
21
+ * @return \Symfony\Component\HttpFoundation\Response|null
22
+ */
23
+ public function onceBasic($field = 'email', $extraConditions = []);
24
+ }
vendor/illuminate/contracts/Auth/UserProvider.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Auth;
4
+
5
+ interface UserProvider
6
+ {
7
+ /**
8
+ * Retrieve a user by their unique identifier.
9
+ *
10
+ * @param mixed $identifier
11
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
12
+ */
13
+ public function retrieveById($identifier);
14
+
15
+ /**
16
+ * Retrieve a user by their unique identifier and "remember me" token.
17
+ *
18
+ * @param mixed $identifier
19
+ * @param string $token
20
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
21
+ */
22
+ public function retrieveByToken($identifier, $token);
23
+
24
+ /**
25
+ * Update the "remember me" token for the given user in storage.
26
+ *
27
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
28
+ * @param string $token
29
+ * @return void
30
+ */
31
+ public function updateRememberToken(Authenticatable $user, $token);
32
+
33
+ /**
34
+ * Retrieve a user by the given credentials.
35
+ *
36
+ * @param array $credentials
37
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
38
+ */
39
+ public function retrieveByCredentials(array $credentials);
40
+
41
+ /**
42
+ * Validate a user against the given credentials.
43
+ *
44
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
45
+ * @param array $credentials
46
+ * @return bool
47
+ */
48
+ public function validateCredentials(Authenticatable $user, array $credentials);
49
+ }
vendor/illuminate/contracts/Broadcasting/Broadcaster.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Broadcasting;
4
+
5
+ interface Broadcaster
6
+ {
7
+ /**
8
+ * Authenticate the incoming request for a given channel.
9
+ *
10
+ * @param \Illuminate\Http\Request $request
11
+ * @return mixed
12
+ */
13
+ public function auth($request);
14
+
15
+ /**
16
+ * Return the valid authentication response.
17
+ *
18
+ * @param \Illuminate\Http\Request $request
19
+ * @param mixed $result
20
+ * @return mixed
21
+ */
22
+ public function validAuthenticationResponse($request, $result);
23
+
24
+ /**
25
+ * Broadcast the given event.
26
+ *
27
+ * @param array $channels
28
+ * @param string $event
29
+ * @param array $payload
30
+ * @return void
31
+ */
32
+ public function broadcast(array $channels, $event, array $payload = []);
33
+ }
vendor/illuminate/contracts/Broadcasting/Factory.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Broadcasting;
4
+
5
+ interface Factory
6
+ {
7
+ /**
8
+ * Get a broadcaster implementation by name.
9
+ *
10
+ * @param string $name
11
+ * @return void
12
+ */
13
+ public function connection($name = null);
14
+ }
vendor/illuminate/contracts/Broadcasting/ShouldBroadcast.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Broadcasting;
4
+
5
+ interface ShouldBroadcast
6
+ {
7
+ /**
8
+ * Get the channels the event should broadcast on.
9
+ *
10
+ * @return array
11
+ */
12
+ public function broadcastOn();
13
+ }
vendor/illuminate/contracts/Broadcasting/ShouldBroadcastNow.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Broadcasting;
4
+
5
+ interface ShouldBroadcastNow extends ShouldBroadcast
6
+ {
7
+ //
8
+ }
vendor/illuminate/contracts/Bus/Dispatcher.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Bus;
4
+
5
+ interface Dispatcher
6
+ {
7
+ /**
8
+ * Dispatch a command to its appropriate handler.
9
+ *
10
+ * @param mixed $command
11
+ * @return mixed
12
+ */
13
+ public function dispatch($command);
14
+
15
+ /**
16
+ * Dispatch a command to its appropriate handler in the current process.
17
+ *
18
+ * @param mixed $command
19
+ * @param mixed $handler
20
+ * @return mixed
21
+ */
22
+ public function dispatchNow($command, $handler = null);
23
+
24
+ /**
25
+ * Set the pipes commands should be piped through before dispatching.
26
+ *
27
+ * @param array $pipes
28
+ * @return $this
29
+ */
30
+ public function pipeThrough(array $pipes);
31
+ }
vendor/illuminate/contracts/Bus/QueueingDispatcher.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Bus;
4
+
5
+ interface QueueingDispatcher extends Dispatcher
6
+ {
7
+ /**
8
+ * Dispatch a command to its appropriate handler behind a queue.
9
+ *
10
+ * @param mixed $command
11
+ * @return mixed
12
+ */
13
+ public function dispatchToQueue($command);
14
+ }
vendor/illuminate/contracts/Cache/Factory.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Cache;
4
+
5
+ interface Factory
6
+ {
7
+ /**
8
+ * Get a cache store instance by name.
9
+ *
10
+ * @param string|null $name
11
+ * @return mixed
12
+ */
13
+ public function store($name = null);
14
+ }
vendor/illuminate/contracts/Cache/Repository.php ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Cache;
4
+
5
+ use Closure;
6
+
7
+ interface Repository
8
+ {
9
+ /**
10
+ * Determine if an item exists in the cache.
11
+ *
12
+ * @param string $key
13
+ * @return bool
14
+ */
15
+ public function has($key);
16
+
17
+ /**
18
+ * Retrieve an item from the cache by key.
19
+ *
20
+ * @param string $key
21
+ * @param mixed $default
22
+ * @return mixed
23
+ */
24
+ public function get($key, $default = null);
25
+
26
+ /**
27
+ * Retrieve an item from the cache and delete it.
28
+ *
29
+ * @param string $key
30
+ * @param mixed $default
31
+ * @return mixed
32
+ */
33
+ public function pull($key, $default = null);
34
+
35
+ /**
36
+ * Store an item in the cache.
37
+ *
38
+ * @param string $key
39
+ * @param mixed $value
40
+ * @param \DateTime|float|int $minutes
41
+ * @return void
42
+ */
43
+ public function put($key, $value, $minutes);
44
+
45
+ /**
46
+ * Store an item in the cache if the key does not exist.
47
+ *
48
+ * @param string $key
49
+ * @param mixed $value
50
+ * @param \DateTime|float|int $minutes
51
+ * @return bool
52
+ */
53
+ public function add($key, $value, $minutes);
54
+
55
+ /**
56
+ * Increment the value of an item in the cache.
57
+ *
58
+ * @param string $key
59
+ * @param mixed $value
60
+ * @return int|bool
61
+ */
62
+ public function increment($key, $value = 1);
63
+
64
+ /**
65
+ * Decrement the value of an item in the cache.
66
+ *
67
+ * @param string $key
68
+ * @param mixed $value
69
+ * @return int|bool
70
+ */
71
+ public function decrement($key, $value = 1);
72
+
73
+ /**
74
+ * Store an item in the cache indefinitely.
75
+ *
76
+ * @param string $key
77
+ * @param mixed $value
78
+ * @return void
79
+ */
80
+ public function forever($key, $value);
81
+
82
+ /**
83
+ * Get an item from the cache, or store the default value.
84
+ *
85
+ * @param string $key
86
+ * @param \DateTime|float|int $minutes
87
+ * @param \Closure $callback
88
+ * @return mixed
89
+ */
90
+ public function remember($key, $minutes, Closure $callback);
91
+
92
+ /**
93
+ * Get an item from the cache, or store the default value forever.
94
+ *
95
+ * @param string $key
96
+ * @param \Closure $callback
97
+ * @return mixed
98
+ */
99
+ public function sear($key, Closure $callback);
100
+
101
+ /**
102
+ * Get an item from the cache, or store the default value forever.
103
+ *
104
+ * @param string $key
105
+ * @param \Closure $callback
106
+ * @return mixed
107
+ */
108
+ public function rememberForever($key, Closure $callback);
109
+
110
+ /**
111
+ * Remove an item from the cache.
112
+ *
113
+ * @param string $key
114
+ * @return bool
115
+ */
116
+ public function forget($key);
117
+ }
vendor/illuminate/contracts/Cache/Store.php ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Cache;
4
+
5
+ interface Store
6
+ {
7
+ /**
8
+ * Retrieve an item from the cache by key.
9
+ *
10
+ * @param string|array $key
11
+ * @return mixed
12
+ */
13
+ public function get($key);
14
+
15
+ /**
16
+ * Retrieve multiple items from the cache by key.
17
+ *
18
+ * Items not found in the cache will have a null value.
19
+ *
20
+ * @param array $keys
21
+ * @return array
22
+ */
23
+ public function many(array $keys);
24
+
25
+ /**
26
+ * Store an item in the cache for a given number of minutes.
27
+ *
28
+ * @param string $key
29
+ * @param mixed $value
30
+ * @param float|int $minutes
31
+ * @return void
32
+ */
33
+ public function put($key, $value, $minutes);
34
+
35
+ /**
36
+ * Store multiple items in the cache for a given number of minutes.
37
+ *
38
+ * @param array $values
39
+ * @param float|int $minutes
40
+ * @return void
41
+ */
42
+ public function putMany(array $values, $minutes);
43
+
44
+ /**
45
+ * Increment the value of an item in the cache.
46
+ *
47
+ * @param string $key
48
+ * @param mixed $value
49
+ * @return int|bool
50
+ */
51
+ public function increment($key, $value = 1);
52
+
53
+ /**
54
+ * Decrement the value of an item in the cache.
55
+ *
56
+ * @param string $key
57
+ * @param mixed $value
58
+ * @return int|bool
59
+ */
60
+ public function decrement($key, $value = 1);
61
+
62
+ /**
63
+ * Store an item in the cache indefinitely.
64
+ *
65
+ * @param string $key
66
+ * @param mixed $value
67
+ * @return void
68
+ */
69
+ public function forever($key, $value);
70
+
71
+ /**
72
+ * Remove an item from the cache.
73
+ *
74
+ * @param string $key
75
+ * @return bool
76
+ */
77
+ public function forget($key);
78
+
79
+ /**
80
+ * Remove all items from the cache.
81
+ *
82
+ * @return bool
83
+ */
84
+ public function flush();
85
+
86
+ /**
87
+ * Get the cache key prefix.
88
+ *
89
+ * @return string
90
+ */
91
+ public function getPrefix();
92
+ }
vendor/illuminate/contracts/Config/Repository.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Config;
4
+
5
+ interface Repository
6
+ {
7
+ /**
8
+ * Determine if the given configuration value exists.
9
+ *
10
+ * @param string $key
11
+ * @return bool
12
+ */
13
+ public function has($key);
14
+
15
+ /**
16
+ * Get the specified configuration value.
17
+ *
18
+ * @param string $key
19
+ * @param mixed $default
20
+ * @return mixed
21
+ */
22
+ public function get($key, $default = null);
23
+
24
+ /**
25
+ * Get all of the configuration items for the application.
26
+ *
27
+ * @return array
28
+ */
29
+ public function all();
30
+
31
+ /**
32
+ * Set a given configuration value.
33
+ *
34
+ * @param array|string $key
35
+ * @param mixed $value
36
+ * @return void
37
+ */
38
+ public function set($key, $value = null);
39
+
40
+ /**
41
+ * Prepend a value onto an array configuration value.
42
+ *
43
+ * @param string $key
44
+ * @param mixed $value
45
+ * @return void
46
+ */
47
+ public function prepend($key, $value);
48
+
49
+ /**
50
+ * Push a value onto an array configuration value.
51
+ *
52
+ * @param string $key
53
+ * @param mixed $value
54
+ * @return void
55
+ */
56
+ public function push($key, $value);
57
+ }
vendor/illuminate/contracts/Console/Application.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Console;
4
+
5
+ interface Application
6
+ {
7
+ /**
8
+ * Call a console application command.
9
+ *
10
+ * @param string $command
11
+ * @param array $parameters
12
+ * @return int
13
+ */
14
+ public function call($command, array $parameters = []);
15
+
16
+ /**
17
+ * Get the output from the last command.
18
+ *
19
+ * @return string
20
+ */
21
+ public function output();
22
+ }
vendor/illuminate/contracts/Console/Kernel.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Console;
4
+
5
+ interface Kernel
6
+ {
7
+ /**
8
+ * Handle an incoming console command.
9
+ *
10
+ * @param \Symfony\Component\Console\Input\InputInterface $input
11
+ * @param \Symfony\Component\Console\Output\OutputInterface $output
12
+ * @return int
13
+ */
14
+ public function handle($input, $output = null);
15
+
16
+ /**
17
+ * Run an Artisan console command by name.
18
+ *
19
+ * @param string $command
20
+ * @param array $parameters
21
+ * @return int
22
+ */
23
+ public function call($command, array $parameters = []);
24
+
25
+ /**
26
+ * Queue an Artisan console command by name.
27
+ *
28
+ * @param string $command
29
+ * @param array $parameters
30
+ * @return \Illuminate\Foundation\Bus\PendingDispatch
31
+ */
32
+ public function queue($command, array $parameters = []);
33
+
34
+ /**
35
+ * Get all of the commands registered with the console.
36
+ *
37
+ * @return array
38
+ */
39
+ public function all();
40
+
41
+ /**
42
+ * Get the output for the last run command.
43
+ *
44
+ * @return string
45
+ */
46
+ public function output();
47
+ }
vendor/illuminate/contracts/Container/BindingResolutionException.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Container;
4
+
5
+ use Exception;
6
+
7
+ class BindingResolutionException extends Exception
8
+ {
9
+ //
10
+ }
vendor/illuminate/contracts/Container/Container.php ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Container;
4
+
5
+ use Closure;
6
+
7
+ interface Container
8
+ {
9
+ /**
10
+ * Determine if the given abstract type has been bound.
11
+ *
12
+ * @param string $abstract
13
+ * @return bool
14
+ */
15
+ public function bound($abstract);
16
+
17
+ /**
18
+ * Alias a type to a different name.
19
+ *
20
+ * @param string $abstract
21
+ * @param string $alias
22
+ * @return void
23
+ */
24
+ public function alias($abstract, $alias);
25
+
26
+ /**
27
+ * Assign a set of tags to a given binding.
28
+ *
29
+ * @param array|string $abstracts
30
+ * @param array|mixed ...$tags
31
+ * @return void
32
+ */
33
+ public function tag($abstracts, $tags);
34
+
35
+ /**
36
+ * Resolve all of the bindings for a given tag.
37
+ *
38
+ * @param array $tag
39
+ * @return array
40
+ */
41
+ public function tagged($tag);
42
+
43
+ /**
44
+ * Register a binding with the container.
45
+ *
46
+ * @param string|array $abstract
47
+ * @param \Closure|string|null $concrete
48
+ * @param bool $shared
49
+ * @return void
50
+ */
51
+ public function bind($abstract, $concrete = null, $shared = false);
52
+
53
+ /**
54
+ * Register a binding if it hasn't already been registered.
55
+ *
56
+ * @param string $abstract
57
+ * @param \Closure|string|null $concrete
58
+ * @param bool $shared
59
+ * @return void
60
+ */
61
+ public function bindIf($abstract, $concrete = null, $shared = false);
62
+
63
+ /**
64
+ * Register a shared binding in the container.
65
+ *
66
+ * @param string|array $abstract
67
+ * @param \Closure|string|null $concrete
68
+ * @return void
69
+ */
70
+ public function singleton($abstract, $concrete = null);
71
+
72
+ /**
73
+ * "Extend" an abstract type in the container.
74
+ *
75
+ * @param string $abstract
76
+ * @param \Closure $closure
77
+ * @return void
78
+ *
79
+ * @throws \InvalidArgumentException
80
+ */
81
+ public function extend($abstract, Closure $closure);
82
+
83
+ /**
84
+ * Register an existing instance as shared in the container.
85
+ *
86
+ * @param string $abstract
87
+ * @param mixed $instance
88
+ * @return void
89
+ */
90
+ public function instance($abstract, $instance);
91
+
92
+ /**
93
+ * Define a contextual binding.
94
+ *
95
+ * @param string $concrete
96
+ * @return \Illuminate\Contracts\Container\ContextualBindingBuilder
97
+ */
98
+ public function when($concrete);
99
+
100
+ /**
101
+ * Get a closure to resolve the given type from the container.
102
+ *
103
+ * @param string $abstract
104
+ * @return \Closure
105
+ */
106
+ public function factory($abstract);
107
+
108
+ /**
109
+ * Resolve the given type from the container.
110
+ *
111
+ * @param string $abstract
112
+ * @return mixed
113
+ */
114
+ public function make($abstract);
115
+
116
+ /**
117
+ * Call the given Closure / class@method and inject its dependencies.
118
+ *
119
+ * @param callable|string $callback
120
+ * @param array $parameters
121
+ * @param string|null $defaultMethod
122
+ * @return mixed
123
+ */
124
+ public function call($callback, array $parameters = [], $defaultMethod = null);
125
+
126
+ /**
127
+ * Determine if the given abstract type has been resolved.
128
+ *
129
+ * @param string $abstract
130
+ * @return bool
131
+ */
132
+ public function resolved($abstract);
133
+
134
+ /**
135
+ * Register a new resolving callback.
136
+ *
137
+ * @param string $abstract
138
+ * @param \Closure|null $callback
139
+ * @return void
140
+ */
141
+ public function resolving($abstract, Closure $callback = null);
142
+
143
+ /**
144
+ * Register a new after resolving callback.
145
+ *
146
+ * @param string $abstract
147
+ * @param \Closure|null $callback
148
+ * @return void
149
+ */
150
+ public function afterResolving($abstract, Closure $callback = null);
151
+ }
vendor/illuminate/contracts/Container/ContextualBindingBuilder.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Container;
4
+
5
+ interface ContextualBindingBuilder
6
+ {
7
+ /**
8
+ * Define the abstract target that depends on the context.
9
+ *
10
+ * @param string $abstract
11
+ * @return $this
12
+ */
13
+ public function needs($abstract);
14
+
15
+ /**
16
+ * Define the implementation for the contextual binding.
17
+ *
18
+ * @param \Closure|string $implementation
19
+ * @return void
20
+ */
21
+ public function give($implementation);
22
+ }
vendor/illuminate/contracts/Cookie/Factory.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Cookie;
4
+
5
+ interface Factory
6
+ {
7
+ /**
8
+ * Create a new cookie instance.
9
+ *
10
+ * @param string $name
11
+ * @param string $value
12
+ * @param int $minutes
13
+ * @param string $path
14
+ * @param string $domain
15
+ * @param bool $secure
16
+ * @param bool $httpOnly
17
+ * @return \Symfony\Component\HttpFoundation\Cookie
18
+ */
19
+ public function make($name, $value, $minutes = 0, $path = null, $domain = null, $secure = false, $httpOnly = true);
20
+
21
+ /**
22
+ * Create a cookie that lasts "forever" (five years).
23
+ *
24
+ * @param string $name
25
+ * @param string $value
26
+ * @param string $path
27
+ * @param string $domain
28
+ * @param bool $secure
29
+ * @param bool $httpOnly
30
+ * @return \Symfony\Component\HttpFoundation\Cookie
31
+ */
32
+ public function forever($name, $value, $path = null, $domain = null, $secure = false, $httpOnly = true);
33
+
34
+ /**
35
+ * Expire the given cookie.
36
+ *
37
+ * @param string $name
38
+ * @param string $path
39
+ * @param string $domain
40
+ * @return \Symfony\Component\HttpFoundation\Cookie
41
+ */
42
+ public function forget($name, $path = null, $domain = null);
43
+ }
vendor/illuminate/contracts/Cookie/QueueingFactory.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Cookie;
4
+
5
+ interface QueueingFactory extends Factory
6
+ {
7
+ /**
8
+ * Queue a cookie to send with the next response.
9
+ *
10
+ * @param array $parameters
11
+ * @return void
12
+ */
13
+ public function queue(...$parameters);
14
+
15
+ /**
16
+ * Remove a cookie from the queue.
17
+ *
18
+ * @param string $name
19
+ */
20
+ public function unqueue($name);
21
+
22
+ /**
23
+ * Get the cookies which have been queued for the next request.
24
+ *
25
+ * @return array
26
+ */
27
+ public function getQueuedCookies();
28
+ }
vendor/illuminate/contracts/Database/ModelIdentifier.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Database;
4
+
5
+ class ModelIdentifier
6
+ {
7
+ /**
8
+ * The class name of the model.
9
+ *
10
+ * @var string
11
+ */
12
+ public $class;
13
+
14
+ /**
15
+ * The unique identifier of the model.
16
+ *
17
+ * This may be either a single ID or an array of IDs.
18
+ *
19
+ * @var mixed
20
+ */
21
+ public $id;
22
+
23
+ /**
24
+ * Create a new model identifier.
25
+ *
26
+ * @param string $class
27
+ * @param mixed $id
28
+ * @return void
29
+ */
30
+ public function __construct($class, $id)
31
+ {
32
+ $this->id = $id;
33
+ $this->class = $class;
34
+ }
35
+ }
vendor/illuminate/contracts/Debug/ExceptionHandler.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Debug;
4
+
5
+ use Exception;
6
+
7
+ interface ExceptionHandler
8
+ {
9
+ /**
10
+ * Report or log an exception.
11
+ *
12
+ * @param \Exception $e
13
+ * @return void
14
+ */
15
+ public function report(Exception $e);
16
+
17
+ /**
18
+ * Render an exception into an HTTP response.
19
+ *
20
+ * @param \Illuminate\Http\Request $request
21
+ * @param \Exception $e
22
+ * @return \Symfony\Component\HttpFoundation\Response
23
+ */
24
+ public function render($request, Exception $e);
25
+
26
+ /**
27
+ * Render an exception to the console.
28
+ *
29
+ * @param \Symfony\Component\Console\Output\OutputInterface $output
30
+ * @param \Exception $e
31
+ * @return void
32
+ */
33
+ public function renderForConsole($output, Exception $e);
34
+ }
vendor/illuminate/contracts/Encryption/DecryptException.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Encryption;
4
+
5
+ use RuntimeException;
6
+
7
+ class DecryptException extends RuntimeException
8
+ {
9
+ //
10
+ }
vendor/illuminate/contracts/Encryption/EncryptException.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Encryption;
4
+
5
+ use RuntimeException;
6
+
7
+ class EncryptException extends RuntimeException
8
+ {
9
+ //
10
+ }
vendor/illuminate/contracts/Encryption/Encrypter.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Encryption;
4
+
5
+ interface Encrypter
6
+ {
7
+ /**
8
+ * Encrypt the given value.
9
+ *
10
+ * @param string $value
11
+ * @param bool $serialize
12
+ * @return string
13
+ */
14
+ public function encrypt($value, $serialize = true);
15
+
16
+ /**
17
+ * Decrypt the given value.
18
+ *
19
+ * @param string $payload
20
+ * @param bool $unserialize
21
+ * @return string
22
+ */
23
+ public function decrypt($payload, $unserialize = true);
24
+ }
vendor/illuminate/contracts/Events/Dispatcher.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Events;
4
+
5
+ interface Dispatcher
6
+ {
7
+ /**
8
+ * Register an event listener with the dispatcher.
9
+ *
10
+ * @param string|array $events
11
+ * @param mixed $listener
12
+ * @return void
13
+ */
14
+ public function listen($events, $listener);
15
+
16
+ /**
17
+ * Determine if a given event has listeners.
18
+ *
19
+ * @param string $eventName
20
+ * @return bool
21
+ */
22
+ public function hasListeners($eventName);
23
+
24
+ /**
25
+ * Register an event subscriber with the dispatcher.
26
+ *
27
+ * @param object|string $subscriber
28
+ * @return void
29
+ */
30
+ public function subscribe($subscriber);
31
+
32
+ /**
33
+ * Dispatch an event until the first non-null response is returned.
34
+ *
35
+ * @param string|object $event
36
+ * @param mixed $payload
37
+ * @return array|null
38
+ */
39
+ public function until($event, $payload = []);
40
+
41
+ /**
42
+ * Dispatch an event and call the listeners.
43
+ *
44
+ * @param string|object $event
45
+ * @param mixed $payload
46
+ * @param bool $halt
47
+ * @return array|null
48
+ */
49
+ public function dispatch($event, $payload = [], $halt = false);
50
+
51
+ /**
52
+ * Register an event and payload to be fired later.
53
+ *
54
+ * @param string $event
55
+ * @param array $payload
56
+ * @return void
57
+ */
58
+ public function push($event, $payload = []);
59
+
60
+ /**
61
+ * Flush a set of pushed events.
62
+ *
63
+ * @param string $event
64
+ * @return void
65
+ */
66
+ public function flush($event);
67
+
68
+ /**
69
+ * Remove a set of listeners from the dispatcher.
70
+ *
71
+ * @param string $event
72
+ * @return void
73
+ */
74
+ public function forget($event);
75
+
76
+ /**
77
+ * Forget all of the queued listeners.
78
+ *
79
+ * @return void
80
+ */
81
+ public function forgetPushed();
82
+ }
vendor/illuminate/contracts/Filesystem/Cloud.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Filesystem;
4
+
5
+ interface Cloud extends Filesystem
6
+ {
7
+ /**
8
+ * Get the URL for the file at the given path.
9
+ *
10
+ * @param string $path
11
+ * @return string
12
+ */
13
+ public function url($path);
14
+ }
vendor/illuminate/contracts/Filesystem/Factory.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Filesystem;
4
+
5
+ interface Factory
6
+ {
7
+ /**
8
+ * Get a filesystem implementation.
9
+ *
10
+ * @param string $name
11
+ * @return \Illuminate\Contracts\Filesystem\Filesystem
12
+ */
13
+ public function disk($name = null);
14
+ }
vendor/illuminate/contracts/Filesystem/FileNotFoundException.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Filesystem;
4
+
5
+ use Exception;
6
+
7
+ class FileNotFoundException extends Exception
8
+ {
9
+ //
10
+ }
vendor/illuminate/contracts/Filesystem/Filesystem.php ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Filesystem;
4
+
5
+ interface Filesystem
6
+ {
7
+ /**
8
+ * The public visibility setting.
9
+ *
10
+ * @var string
11
+ */
12
+ const VISIBILITY_PUBLIC = 'public';
13
+
14
+ /**
15
+ * The private visibility setting.
16
+ *
17
+ * @var string
18
+ */
19
+ const VISIBILITY_PRIVATE = 'private';
20
+
21
+ /**
22
+ * Determine if a file exists.
23
+ *
24
+ * @param string $path
25
+ * @return bool
26
+ */
27
+ public function exists($path);
28
+
29
+ /**
30
+ * Get the contents of a file.
31
+ *
32
+ * @param string $path
33
+ * @return string
34
+ *
35
+ * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
36
+ */
37
+ public function get($path);
38
+
39
+ /**
40
+ * Write the contents of a file.
41
+ *
42
+ * @param string $path
43
+ * @param string|resource $contents
44
+ * @param string $visibility
45
+ * @return bool
46
+ */
47
+ public function put($path, $contents, $visibility = null);
48
+
49
+ /**
50
+ * Get the visibility for the given path.
51
+ *
52
+ * @param string $path
53
+ * @return string
54
+ */
55
+ public function getVisibility($path);
56
+
57
+ /**
58
+ * Set the visibility for the given path.
59
+ *
60
+ * @param string $path
61
+ * @param string $visibility
62
+ * @return void
63
+ */
64
+ public function setVisibility($path, $visibility);
65
+
66
+ /**
67
+ * Prepend to a file.
68
+ *
69
+ * @param string $path
70
+ * @param string $data
71
+ * @return int
72
+ */
73
+ public function prepend($path, $data);
74
+
75
+ /**
76
+ * Append to a file.
77
+ *
78
+ * @param string $path
79
+ * @param string $data
80
+ * @return int
81
+ */
82
+ public function append($path, $data);
83
+
84
+ /**
85
+ * Delete the file at a given path.
86
+ *
87
+ * @param string|array $paths
88
+ * @return bool
89
+ */
90
+ public function delete($paths);
91
+
92
+ /**
93
+ * Copy a file to a new location.
94
+ *
95
+ * @param string $from
96
+ * @param string $to
97
+ * @return bool
98
+ */
99
+ public function copy($from, $to);
100
+
101
+ /**
102
+ * Move a file to a new location.
103
+ *
104
+ * @param string $from
105
+ * @param string $to
106
+ * @return bool
107
+ */
108
+ public function move($from, $to);
109
+
110
+ /**
111
+ * Get the file size of a given file.
112
+ *
113
+ * @param string $path
114
+ * @return int
115
+ */
116
+ public function size($path);
117
+
118
+ /**
119
+ * Get the file's last modification time.
120
+ *
121
+ * @param string $path
122
+ * @return int
123
+ */
124
+ public function lastModified($path);
125
+
126
+ /**
127
+ * Get an array of all files in a directory.
128
+ *
129
+ * @param string|null $directory
130
+ * @param bool $recursive
131
+ * @return array
132
+ */
133
+ public function files($directory = null, $recursive = false);
134
+
135
+ /**
136
+ * Get all of the files from the given directory (recursive).
137
+ *
138
+ * @param string|null $directory
139
+ * @return array
140
+ */
141
+ public function allFiles($directory = null);
142
+
143
+ /**
144
+ * Get all of the directories within a given directory.
145
+ *
146
+ * @param string|null $directory
147
+ * @param bool $recursive
148
+ * @return array
149
+ */
150
+ public function directories($directory = null, $recursive = false);
151
+
152
+ /**
153
+ * Get all (recursive) of the directories within a given directory.
154
+ *
155
+ * @param string|null $directory
156
+ * @return array
157
+ */
158
+ public function allDirectories($directory = null);
159
+
160
+ /**
161
+ * Create a directory.
162
+ *
163
+ * @param string $path
164
+ * @return bool
165
+ */
166
+ public function makeDirectory($path);
167
+
168
+ /**
169
+ * Recursively delete a directory.
170
+ *
171
+ * @param string $directory
172
+ * @return bool
173
+ */
174
+ public function deleteDirectory($directory);
175
+ }
vendor/illuminate/contracts/Foundation/Application.php ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Foundation;
4
+
5
+ use Illuminate\Contracts\Container\Container;
6
+
7
+ interface Application extends Container
8
+ {
9
+ /**
10
+ * Get the version number of the application.
11
+ *
12
+ * @return string
13
+ */
14
+ public function version();
15
+
16
+ /**
17
+ * Get the base path of the Laravel installation.
18
+ *
19
+ * @return string
20
+ */
21
+ public function basePath();
22
+
23
+ /**
24
+ * Get or check the current application environment.
25
+ *
26
+ * @return string
27
+ */
28
+ public function environment();
29
+
30
+ /**
31
+ * Determine if the application is currently down for maintenance.
32
+ *
33
+ * @return bool
34
+ */
35
+ public function isDownForMaintenance();
36
+
37
+ /**
38
+ * Register all of the configured providers.
39
+ *
40
+ * @return void
41
+ */
42
+ public function registerConfiguredProviders();
43
+
44
+ /**
45
+ * Register a service provider with the application.
46
+ *
47
+ * @param \Illuminate\Support\ServiceProvider|string $provider
48
+ * @param array $options
49
+ * @param bool $force
50
+ * @return \Illuminate\Support\ServiceProvider
51
+ */
52
+ public function register($provider, $options = [], $force = false);
53
+
54
+ /**
55
+ * Register a deferred provider and service.
56
+ *
57
+ * @param string $provider
58
+ * @param string|null $service
59
+ * @return void
60
+ */
61
+ public function registerDeferredProvider($provider, $service = null);
62
+
63
+ /**
64
+ * Boot the application's service providers.
65
+ *
66
+ * @return void
67
+ */
68
+ public function boot();
69
+
70
+ /**
71
+ * Register a new boot listener.
72
+ *
73
+ * @param mixed $callback
74
+ * @return void
75
+ */
76
+ public function booting($callback);
77
+
78
+ /**
79
+ * Register a new "booted" listener.
80
+ *
81
+ * @param mixed $callback
82
+ * @return void
83
+ */
84
+ public function booted($callback);
85
+
86
+ /**
87
+ * Get the path to the cached services.php file.
88
+ *
89
+ * @return string
90
+ */
91
+ public function getCachedServicesPath();
92
+ }
vendor/illuminate/contracts/Hashing/Hasher.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Hashing;
4
+
5
+ interface Hasher
6
+ {
7
+ /**
8
+ * Hash the given value.
9
+ *
10
+ * @param string $value
11
+ * @param array $options
12
+ * @return string
13
+ */
14
+ public function make($value, array $options = []);
15
+
16
+ /**
17
+ * Check the given plain value against a hash.
18
+ *
19
+ * @param string $value
20
+ * @param string $hashedValue
21
+ * @param array $options
22
+ * @return bool
23
+ */
24
+ public function check($value, $hashedValue, array $options = []);
25
+
26
+ /**
27
+ * Check if the given hash has been hashed using the given options.
28
+ *
29
+ * @param string $hashedValue
30
+ * @param array $options
31
+ * @return bool
32
+ */
33
+ public function needsRehash($hashedValue, array $options = []);
34
+ }
vendor/illuminate/contracts/Http/Kernel.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Http;
4
+
5
+ interface Kernel
6
+ {
7
+ /**
8
+ * Bootstrap the application for HTTP requests.
9
+ *
10
+ * @return void
11
+ */
12
+ public function bootstrap();
13
+
14
+ /**
15
+ * Handle an incoming HTTP request.
16
+ *
17
+ * @param \Symfony\Component\HttpFoundation\Request $request
18
+ * @return \Symfony\Component\HttpFoundation\Response
19
+ */
20
+ public function handle($request);
21
+
22
+ /**
23
+ * Perform any final actions for the request lifecycle.
24
+ *
25
+ * @param \Symfony\Component\HttpFoundation\Request $request
26
+ * @param \Symfony\Component\HttpFoundation\Response $response
27
+ * @return void
28
+ */
29
+ public function terminate($request, $response);
30
+
31
+ /**
32
+ * Get the Laravel application instance.
33
+ *
34
+ * @return \Illuminate\Contracts\Foundation\Application
35
+ */
36
+ public function getApplication();
37
+ }
vendor/illuminate/contracts/Logging/Log.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Logging;
4
+
5
+ interface Log
6
+ {
7
+ /**
8
+ * Log an alert message to the logs.
9
+ *
10
+ * @param string $message
11
+ * @param array $context
12
+ * @return void
13
+ */
14
+ public function alert($message, array $context = []);
15
+
16
+ /**
17
+ * Log a critical message to the logs.
18
+ *
19
+ * @param string $message
20
+ * @param array $context
21
+ * @return void
22
+ */
23
+ public function critical($message, array $context = []);
24
+
25
+ /**
26
+ * Log an error message to the logs.
27
+ *
28
+ * @param string $message
29
+ * @param array $context
30
+ * @return void
31
+ */
32
+ public function error($message, array $context = []);
33
+
34
+ /**
35
+ * Log a warning message to the logs.
36
+ *
37
+ * @param string $message
38
+ * @param array $context
39
+ * @return void
40
+ */
41
+ public function warning($message, array $context = []);
42
+
43
+ /**
44
+ * Log a notice to the logs.
45
+ *
46
+ * @param string $message
47
+ * @param array $context
48
+ * @return void
49
+ */
50
+ public function notice($message, array $context = []);
51
+
52
+ /**
53
+ * Log an informational message to the logs.
54
+ *
55
+ * @param string $message
56
+ * @param array $context
57
+ * @return void
58
+ */
59
+ public function info($message, array $context = []);
60
+
61
+ /**
62
+ * Log a debug message to the logs.
63
+ *
64
+ * @param string $message
65
+ * @param array $context
66
+ * @return void
67
+ */
68
+ public function debug($message, array $context = []);
69
+
70
+ /**
71
+ * Log a message to the logs.
72
+ *
73
+ * @param string $level
74
+ * @param string $message
75
+ * @param array $context
76
+ * @return void
77
+ */
78
+ public function log($level, $message, array $context = []);
79
+
80
+ /**
81
+ * Register a file log handler.
82
+ *
83
+ * @param string $path
84
+ * @param string $level
85
+ * @return void
86
+ */
87
+ public function useFiles($path, $level = 'debug');
88
+
89
+ /**
90
+ * Register a daily file log handler.
91
+ *
92
+ * @param string $path
93
+ * @param int $days
94
+ * @param string $level
95
+ * @return void
96
+ */
97
+ public function useDailyFiles($path, $days = 0, $level = 'debug');
98
+ }
vendor/illuminate/contracts/Mail/MailQueue.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Mail;
4
+
5
+ interface MailQueue
6
+ {
7
+ /**
8
+ * Queue a new e-mail message for sending.
9
+ *
10
+ * @param string|array $view
11
+ * @param array $data
12
+ * @param \Closure|string $callback
13
+ * @param string $queue
14
+ * @return mixed
15
+ */
16
+ public function queue($view, array $data, $callback, $queue = null);
17
+
18
+ /**
19
+ * Queue a new e-mail message for sending after (n) seconds.
20
+ *
21
+ * @param int $delay
22
+ * @param string|array $view
23
+ * @param array $data
24
+ * @param \Closure|string $callback
25
+ * @param string $queue
26
+ * @return mixed
27
+ */
28
+ public function later($delay, $view, array $data, $callback, $queue = null);
29
+ }
vendor/illuminate/contracts/Mail/Mailable.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Mail;
4
+
5
+ use Illuminate\Contracts\Queue\Factory as Queue;
6
+
7
+ interface Mailable
8
+ {
9
+ /**
10
+ * Send the message using the given mailer.
11
+ *
12
+ * @param Mailer $mailer
13
+ * @return void
14
+ */
15
+ public function send(Mailer $mailer);
16
+
17
+ /**
18
+ * Queue the given message.
19
+ *
20
+ * @param Queue $queue
21
+ * @return mixed
22
+ */
23
+ public function queue(Queue $queue);
24
+
25
+ /**
26
+ * Deliver the queued message after the given delay.
27
+ *
28
+ * @param \DateTime|int $delay
29
+ * @param Queue $queue
30
+ * @return mixed
31
+ */
32
+ public function later($delay, Queue $queue);
33
+ }
vendor/illuminate/contracts/Mail/Mailer.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Mail;
4
+
5
+ interface Mailer
6
+ {
7
+ /**
8
+ * Send a new message when only a raw text part.
9
+ *
10
+ * @param string $text
11
+ * @param \Closure|string $callback
12
+ * @return int
13
+ */
14
+ public function raw($text, $callback);
15
+
16
+ /**
17
+ * Send a new message using a view.
18
+ *
19
+ * @param string|array $view
20
+ * @param array $data
21
+ * @param \Closure|string $callback
22
+ * @return void
23
+ */
24
+ public function send($view, array $data = [], $callback = null);
25
+
26
+ /**
27
+ * Get the array of failed recipients.
28
+ *
29
+ * @return array
30
+ */
31
+ public function failures();
32
+ }
vendor/illuminate/contracts/Notifications/Dispatcher.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Notifications;
4
+
5
+ interface Dispatcher
6
+ {
7
+ /**
8
+ * Send the given notification to the given notifiable entities.
9
+ *
10
+ * @param \Illuminate\Support\Collection|array|mixed $notifiables
11
+ * @param mixed $notification
12
+ * @return void
13
+ */
14
+ public function send($notifiables, $notification);
15
+
16
+ /**
17
+ * Send the given notification immediately.
18
+ *
19
+ * @param \Illuminate\Support\Collection|array|mixed $notifiables
20
+ * @param mixed $notification
21
+ * @return void
22
+ */
23
+ public function sendNow($notifiables, $notification);
24
+ }
vendor/illuminate/contracts/Notifications/Factory.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Notifications;
4
+
5
+ interface Factory
6
+ {
7
+ /**
8
+ * Get a channel instance by name.
9
+ *
10
+ * @param string|null $name
11
+ * @return mixed
12
+ */
13
+ public function channel($name = null);
14
+
15
+ /**
16
+ * Send the given notification to the given notifiable entities.
17
+ *
18
+ * @param \Illuminate\Support\Collection|array|mixed $notifiables
19
+ * @param mixed $notification
20
+ * @return void
21
+ */
22
+ public function send($notifiables, $notification);
23
+
24
+ /**
25
+ * Send the given notification immediately.
26
+ *
27
+ * @param \Illuminate\Support\Collection|array|mixed $notifiables
28
+ * @param mixed $notification
29
+ * @return void
30
+ */
31
+ public function sendNow($notifiables, $notification);
32
+ }
vendor/illuminate/contracts/Pagination/LengthAwarePaginator.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Pagination;
4
+
5
+ interface LengthAwarePaginator extends Paginator
6
+ {
7
+ /**
8
+ * Create a range of pagination URLs.
9
+ *
10
+ * @param int $start
11
+ * @param int $end
12
+ * @return array
13
+ */
14
+ public function getUrlRange($start, $end);
15
+
16
+ /**
17
+ * Determine the total number of items in the data store.
18
+ *
19
+ * @return int
20
+ */
21
+ public function total();
22
+
23
+ /**
24
+ * Get the page number of the last available page.
25
+ *
26
+ * @return int
27
+ */
28
+ public function lastPage();
29
+ }
vendor/illuminate/contracts/Pagination/Paginator.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Pagination;
4
+
5
+ interface Paginator
6
+ {
7
+ /**
8
+ * Get the URL for a given page.
9
+ *
10
+ * @param int $page
11
+ * @return string
12
+ */
13
+ public function url($page);
14
+
15
+ /**
16
+ * Add a set of query string values to the paginator.
17
+ *
18
+ * @param array|string $key
19
+ * @param string|null $value
20
+ * @return $this
21
+ */
22
+ public function appends($key, $value = null);
23
+
24
+ /**
25
+ * Get / set the URL fragment to be appended to URLs.
26
+ *
27
+ * @param string|null $fragment
28
+ * @return $this|string
29
+ */
30
+ public function fragment($fragment = null);
31
+
32
+ /**
33
+ * The URL for the next page, or null.
34
+ *
35
+ * @return string|null
36
+ */
37
+ public function nextPageUrl();
38
+
39
+ /**
40
+ * Get the URL for the previous page, or null.
41
+ *
42
+ * @return string|null
43
+ */
44
+ public function previousPageUrl();
45
+
46
+ /**
47
+ * Get all of the items being paginated.
48
+ *
49
+ * @return array
50
+ */
51
+ public function items();
52
+
53
+ /**
54
+ * Get the "index" of the first item being paginated.
55
+ *
56
+ * @return int
57
+ */
58
+ public function firstItem();
59
+
60
+ /**
61
+ * Get the "index" of the last item being paginated.
62
+ *
63
+ * @return int
64
+ */
65
+ public function lastItem();
66
+
67
+ /**
68
+ * Determine how many items are being shown per page.
69
+ *
70
+ * @return int
71
+ */
72
+ public function perPage();
73
+
74
+ /**
75
+ * Determine the current page being paginated.
76
+ *
77
+ * @return int
78
+ */
79
+ public function currentPage();
80
+
81
+ /**
82
+ * Determine if there are enough items to split into multiple pages.
83
+ *
84
+ * @return bool
85
+ */
86
+ public function hasPages();
87
+
88
+ /**
89
+ * Determine if there is more items in the data store.
90
+ *
91
+ * @return bool
92
+ */
93
+ public function hasMorePages();
94
+
95
+ /**
96
+ * Determine if the list of items is empty or not.
97
+ *
98
+ * @return bool
99
+ */
100
+ public function isEmpty();
101
+
102
+ /**
103
+ * Render the paginator using a given view.
104
+ *
105
+ * @param string|null $view
106
+ * @param array $data
107
+ * @return string
108
+ */
109
+ public function render($view = null, $data = []);
110
+ }
vendor/illuminate/contracts/Pipeline/Hub.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Pipeline;
4
+
5
+ interface Hub
6
+ {
7
+ /**
8
+ * Send an object through one of the available pipelines.
9
+ *
10
+ * @param mixed $object
11
+ * @param string|null $pipeline
12
+ * @return mixed
13
+ */
14
+ public function pipe($object, $pipeline = null);
15
+ }
vendor/illuminate/contracts/Pipeline/Pipeline.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Pipeline;
4
+
5
+ use Closure;
6
+
7
+ interface Pipeline
8
+ {
9
+ /**
10
+ * Set the traveler object being sent on the pipeline.
11
+ *
12
+ * @param mixed $traveler
13
+ * @return $this
14
+ */
15
+ public function send($traveler);
16
+
17
+ /**
18
+ * Set the stops of the pipeline.
19
+ *
20
+ * @param dynamic|array $stops
21
+ * @return $this
22
+ */
23
+ public function through($stops);
24
+
25
+ /**
26
+ * Set the method to call on the stops.
27
+ *
28
+ * @param string $method
29
+ * @return $this
30
+ */
31
+ public function via($method);
32
+
33
+ /**
34
+ * Run the pipeline with a final destination callback.
35
+ *
36
+ * @param \Closure $destination
37
+ * @return mixed
38
+ */
39
+ public function then(Closure $destination);
40
+ }
vendor/illuminate/contracts/Queue/EntityNotFoundException.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Queue;
4
+
5
+ use InvalidArgumentException;
6
+
7
+ class EntityNotFoundException extends InvalidArgumentException
8
+ {
9
+ /**
10
+ * Create a new exception instance.
11
+ *
12
+ * @param string $type
13
+ * @param mixed $id
14
+ * @return void
15
+ */
16
+ public function __construct($type, $id)
17
+ {
18
+ $id = (string) $id;
19
+
20
+ parent::__construct("Queueable entity [{$type}] not found for ID [{$id}].");
21
+ }
22
+ }
vendor/illuminate/contracts/Queue/EntityResolver.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Queue;
4
+
5
+ interface EntityResolver
6
+ {
7
+ /**
8
+ * Resolve the entity for the given ID.
9
+ *
10
+ * @param string $type
11
+ * @param mixed $id
12
+ * @return mixed
13
+ */
14
+ public function resolve($type, $id);
15
+ }
vendor/illuminate/contracts/Queue/Factory.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Queue;
4
+
5
+ interface Factory
6
+ {
7
+ /**
8
+ * Resolve a queue connection instance.
9
+ *
10
+ * @param string $name
11
+ * @return \Illuminate\Contracts\Queue\Queue
12
+ */
13
+ public function connection($name = null);
14
+ }
vendor/illuminate/contracts/Queue/Job.php ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Queue;
4
+
5
+ interface Job
6
+ {
7
+ /**
8
+ * Fire the job.
9
+ *
10
+ * @return void
11
+ */
12
+ public function fire();
13
+
14
+ /**
15
+ * Release the job back into the queue.
16
+ *
17
+ * @param int $delay
18
+ * @return mixed
19
+ */
20
+ public function release($delay = 0);
21
+
22
+ /**
23
+ * Delete the job from the queue.
24
+ *
25
+ * @return void
26
+ */
27
+ public function delete();
28
+
29
+ /**
30
+ * Determine if the job has been deleted.
31
+ *
32
+ * @return bool
33
+ */
34
+ public function isDeleted();
35
+
36
+ /**
37
+ * Determine if the job has been deleted or released.
38
+ *
39
+ * @return bool
40
+ */
41
+ public function isDeletedOrReleased();
42
+
43
+ /**
44
+ * Get the number of times the job has been attempted.
45
+ *
46
+ * @return int
47
+ */
48
+ public function attempts();
49
+
50
+ /**
51
+ * Process an exception that caused the job to fail.
52
+ *
53
+ * @param \Throwable $e
54
+ * @return void
55
+ */
56
+ public function failed($e);
57
+
58
+ /**
59
+ * The number of times to attempt a job.
60
+ *
61
+ * @return int|null
62
+ */
63
+ public function maxTries();
64
+
65
+ /**
66
+ * The number of seconds the job can run.
67
+ *
68
+ * @return int|null
69
+ */
70
+ public function timeout();
71
+
72
+ /**
73
+ * Get the name of the queued job class.
74
+ *
75
+ * @return string
76
+ */
77
+ public function getName();
78
+
79
+ /**
80
+ * Get the resolved name of the queued job class.
81
+ *
82
+ * Resolves the name of "wrapped" jobs such as class-based handlers.
83
+ *
84
+ * @return string
85
+ */
86
+ public function resolveName();
87
+
88
+ /**
89
+ * Get the name of the connection the job belongs to.
90
+ *
91
+ * @return string
92
+ */
93
+ public function getConnectionName();
94
+
95
+ /**
96
+ * Get the name of the queue the job belongs to.
97
+ *
98
+ * @return string
99
+ */
100
+ public function getQueue();
101
+
102
+ /**
103
+ * Get the raw body string for the job.
104
+ *
105
+ * @return string
106
+ */
107
+ public function getRawBody();
108
+ }
vendor/illuminate/contracts/Queue/Monitor.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Queue;
4
+
5
+ interface Monitor
6
+ {
7
+ /**
8
+ * Register a callback to be executed on every iteration through the queue loop.
9
+ *
10
+ * @param mixed $callback
11
+ * @return void
12
+ */
13
+ public function looping($callback);
14
+
15
+ /**
16
+ * Register a callback to be executed when a job fails after the maximum amount of retries.
17
+ *
18
+ * @param mixed $callback
19
+ * @return void
20
+ */
21
+ public function failing($callback);
22
+
23
+ /**
24
+ * Register a callback to be executed when a daemon queue is stopping.
25
+ *
26
+ * @param mixed $callback
27
+ * @return void
28
+ */
29
+ public function stopping($callback);
30
+ }
vendor/illuminate/contracts/Queue/Queue.php ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Queue;
4
+
5
+ interface Queue
6
+ {
7
+ /**
8
+ * Get the size of the queue.
9
+ *
10
+ * @param string $queue
11
+ * @return int
12
+ */
13
+ public function size($queue = null);
14
+
15
+ /**
16
+ * Push a new job onto the queue.
17
+ *
18
+ * @param string $job
19
+ * @param mixed $data
20
+ * @param string $queue
21
+ * @return mixed
22
+ */
23
+ public function push($job, $data = '', $queue = null);
24
+
25
+ /**
26
+ * Push a new job onto the queue.
27
+ *
28
+ * @param string $queue
29
+ * @param string $job
30
+ * @param mixed $data
31
+ * @return mixed
32
+ */
33
+ public function pushOn($queue, $job, $data = '');
34
+
35
+ /**
36
+ * Push a raw payload onto the queue.
37
+ *
38
+ * @param string $payload
39
+ * @param string $queue
40
+ * @param array $options
41
+ * @return mixed
42
+ */
43
+ public function pushRaw($payload, $queue = null, array $options = []);
44
+
45
+ /**
46
+ * Push a new job onto the queue after a delay.
47
+ *
48
+ * @param \DateTime|int $delay
49
+ * @param string $job
50
+ * @param mixed $data
51
+ * @param string $queue
52
+ * @return mixed
53
+ */
54
+ public function later($delay, $job, $data = '', $queue = null);
55
+
56
+ /**
57
+ * Push a new job onto the queue after a delay.
58
+ *
59
+ * @param string $queue
60
+ * @param \DateTime|int $delay
61
+ * @param string $job
62
+ * @param mixed $data
63
+ * @return mixed
64
+ */
65
+ public function laterOn($queue, $delay, $job, $data = '');
66
+
67
+ /**
68
+ * Push an array of jobs onto the queue.
69
+ *
70
+ * @param array $jobs
71
+ * @param mixed $data
72
+ * @param string $queue
73
+ * @return mixed
74
+ */
75
+ public function bulk($jobs, $data = '', $queue = null);
76
+
77
+ /**
78
+ * Pop the next job off of the queue.
79
+ *
80
+ * @param string $queue
81
+ * @return \Illuminate\Contracts\Queue\Job|null
82
+ */
83
+ public function pop($queue = null);
84
+
85
+ /**
86
+ * Get the connection name for the queue.
87
+ *
88
+ * @return string
89
+ */
90
+ public function getConnectionName();
91
+
92
+ /**
93
+ * Set the connection name for the queue.
94
+ *
95
+ * @param string $name
96
+ * @return $this
97
+ */
98
+ public function setConnectionName($name);
99
+ }
vendor/illuminate/contracts/Queue/QueueableCollection.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Queue;
4
+
5
+ interface QueueableCollection
6
+ {
7
+ /**
8
+ * Get the type of the entities being queued.
9
+ *
10
+ * @return string|null
11
+ */
12
+ public function getQueueableClass();
13
+
14
+ /**
15
+ * Get the identifiers for all of the entities.
16
+ *
17
+ * @return array
18
+ */
19
+ public function getQueueableIds();
20
+ }
vendor/illuminate/contracts/Queue/QueueableEntity.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Queue;
4
+
5
+ interface QueueableEntity
6
+ {
7
+ /**
8
+ * Get the queueable identity for the entity.
9
+ *
10
+ * @return mixed
11
+ */
12
+ public function getQueueableId();
13
+ }
vendor/illuminate/contracts/Queue/ShouldQueue.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Queue;
4
+
5
+ interface ShouldQueue
6
+ {
7
+ //
8
+ }
vendor/illuminate/contracts/Redis/Factory.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Redis;
4
+
5
+ interface Factory
6
+ {
7
+ /**
8
+ * Get a Redis connection by name.
9
+ *
10
+ * @param string $name
11
+ * @return \Illuminate\Redis\Connections\Connection
12
+ */
13
+ public function connection($name = null);
14
+ }
vendor/illuminate/contracts/Routing/BindingRegistrar.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Routing;
4
+
5
+ interface BindingRegistrar
6
+ {
7
+ /**
8
+ * Add a new route parameter binder.
9
+ *
10
+ * @param string $key
11
+ * @param string|callable $binder
12
+ * @return void
13
+ */
14
+ public function bind($key, $binder);
15
+
16
+ /**
17
+ * Get the binding callback for a given binding.
18
+ *
19
+ * @param string $key
20
+ * @return \Closure
21
+ */
22
+ public function getBindingCallback($key);
23
+ }
vendor/illuminate/contracts/Routing/Registrar.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Routing;
4
+
5
+ interface Registrar
6
+ {
7
+ /**
8
+ * Register a new GET route with the router.
9
+ *
10
+ * @param string $uri
11
+ * @param \Closure|array|string $action
12
+ * @return \Illuminate\Routing\Route
13
+ */
14
+ public function get($uri, $action);
15
+
16
+ /**
17
+ * Register a new POST route with the router.
18
+ *
19
+ * @param string $uri
20
+ * @param \Closure|array|string $action
21
+ * @return \Illuminate\Routing\Route
22
+ */
23
+ public function post($uri, $action);
24
+
25
+ /**
26
+ * Register a new PUT route with the router.
27
+ *
28
+ * @param string $uri
29
+ * @param \Closure|array|string $action
30
+ * @return \Illuminate\Routing\Route
31
+ */
32
+ public function put($uri, $action);
33
+
34
+ /**
35
+ * Register a new DELETE route with the router.
36
+ *
37
+ * @param string $uri
38
+ * @param \Closure|array|string $action
39
+ * @return \Illuminate\Routing\Route
40
+ */
41
+ public function delete($uri, $action);
42
+
43
+ /**
44
+ * Register a new PATCH route with the router.
45
+ *
46
+ * @param string $uri
47
+ * @param \Closure|array|string $action
48
+ * @return \Illuminate\Routing\Route
49
+ */
50
+ public function patch($uri, $action);
51
+
52
+ /**
53
+ * Register a new OPTIONS route with the router.
54
+ *
55
+ * @param string $uri
56
+ * @param \Closure|array|string $action
57
+ * @return \Illuminate\Routing\Route
58
+ */
59
+ public function options($uri, $action);
60
+
61
+ /**
62
+ * Register a new route with the given verbs.
63
+ *
64
+ * @param array|string $methods
65
+ * @param string $uri
66
+ * @param \Closure|array|string $action
67
+ * @return \Illuminate\Routing\Route
68
+ */
69
+ public function match($methods, $uri, $action);
70
+
71
+ /**
72
+ * Route a resource to a controller.
73
+ *
74
+ * @param string $name
75
+ * @param string $controller
76
+ * @param array $options
77
+ * @return void
78
+ */
79
+ public function resource($name, $controller, array $options = []);
80
+
81
+ /**
82
+ * Create a route group with shared attributes.
83
+ *
84
+ * @param array $attributes
85
+ * @param \Closure|string $routes
86
+ * @return void
87
+ */
88
+ public function group(array $attributes, $routes);
89
+
90
+ /**
91
+ * Substitute the route bindings onto the route.
92
+ *
93
+ * @param \Illuminate\Routing\Route $route
94
+ * @return \Illuminate\Routing\Route
95
+ */
96
+ public function substituteBindings($route);
97
+
98
+ /**
99
+ * Substitute the implicit Eloquent model bindings for the route.
100
+ *
101
+ * @param \Illuminate\Routing\Route $route
102
+ * @return void
103
+ */
104
+ public function substituteImplicitBindings($route);
105
+ }
vendor/illuminate/contracts/Routing/ResponseFactory.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Routing;
4
+
5
+ interface ResponseFactory
6
+ {
7
+ /**
8
+ * Return a new response from the application.
9
+ *
10
+ * @param string $content
11
+ * @param int $status
12
+ * @param array $headers
13
+ * @return \Illuminate\Http\Response
14
+ */
15
+ public function make($content = '', $status = 200, array $headers = []);
16
+
17
+ /**
18
+ * Return a new view response from the application.
19
+ *
20
+ * @param string $view
21
+ * @param array $data
22
+ * @param int $status
23
+ * @param array $headers
24
+ * @return \Illuminate\Http\Response
25
+ */
26
+ public function view($view, $data = [], $status = 200, array $headers = []);
27
+
28
+ /**
29
+ * Return a new JSON response from the application.
30
+ *
31
+ * @param string|array $data
32
+ * @param int $status
33
+ * @param array $headers
34
+ * @param int $options
35
+ * @return \Illuminate\Http\JsonResponse
36
+ */
37
+ public function json($data = [], $status = 200, array $headers = [], $options = 0);
38
+
39
+ /**
40
+ * Return a new JSONP response from the application.
41
+ *
42
+ * @param string $callback
43
+ * @param string|array $data
44
+ * @param int $status
45
+ * @param array $headers
46
+ * @param int $options
47
+ * @return \Illuminate\Http\JsonResponse
48
+ */
49
+ public function jsonp($callback, $data = [], $status = 200, array $headers = [], $options = 0);
50
+
51
+ /**
52
+ * Return a new streamed response from the application.
53
+ *
54
+ * @param \Closure $callback
55
+ * @param int $status
56
+ * @param array $headers
57
+ * @return \Symfony\Component\HttpFoundation\StreamedResponse
58
+ */
59
+ public function stream($callback, $status = 200, array $headers = []);
60
+
61
+ /**
62
+ * Create a new file download response.
63
+ *
64
+ * @param \SplFileInfo|string $file
65
+ * @param string $name
66
+ * @param array $headers
67
+ * @param string|null $disposition
68
+ * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
69
+ */
70
+ public function download($file, $name = null, array $headers = [], $disposition = 'attachment');
71
+
72
+ /**
73
+ * Create a new redirect response to the given path.
74
+ *
75
+ * @param string $path
76
+ * @param int $status
77
+ * @param array $headers
78
+ * @param bool|null $secure
79
+ * @return \Illuminate\Http\RedirectResponse
80
+ */
81
+ public function redirectTo($path, $status = 302, $headers = [], $secure = null);
82
+
83
+ /**
84
+ * Create a new redirect response to a named route.
85
+ *
86
+ * @param string $route
87
+ * @param array $parameters
88
+ * @param int $status
89
+ * @param array $headers
90
+ * @return \Illuminate\Http\RedirectResponse
91
+ */
92
+ public function redirectToRoute($route, $parameters = [], $status = 302, $headers = []);
93
+
94
+ /**
95
+ * Create a new redirect response to a controller action.
96
+ *
97
+ * @param string $action
98
+ * @param array $parameters
99
+ * @param int $status
100
+ * @param array $headers
101
+ * @return \Illuminate\Http\RedirectResponse
102
+ */
103
+ public function redirectToAction($action, $parameters = [], $status = 302, $headers = []);
104
+
105
+ /**
106
+ * Create a new redirect response, while putting the current URL in the session.
107
+ *
108
+ * @param string $path
109
+ * @param int $status
110
+ * @param array $headers
111
+ * @param bool|null $secure
112
+ * @return \Illuminate\Http\RedirectResponse
113
+ */
114
+ public function redirectGuest($path, $status = 302, $headers = [], $secure = null);
115
+
116
+ /**
117
+ * Create a new redirect response to the previously intended location.
118
+ *
119
+ * @param string $default
120
+ * @param int $status
121
+ * @param array $headers
122
+ * @param bool|null $secure
123
+ * @return \Illuminate\Http\RedirectResponse
124
+ */
125
+ public function redirectToIntended($default = '/', $status = 302, $headers = [], $secure = null);
126
+ }
vendor/illuminate/contracts/Routing/UrlGenerator.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Routing;
4
+
5
+ interface UrlGenerator
6
+ {
7
+ /**
8
+ * Get the current URL for the request.
9
+ *
10
+ * @return string
11
+ */
12
+ public function current();
13
+
14
+ /**
15
+ * Generate an absolute URL to the given path.
16
+ *
17
+ * @param string $path
18
+ * @param mixed $extra
19
+ * @param bool $secure
20
+ * @return string
21
+ */
22
+ public function to($path, $extra = [], $secure = null);
23
+
24
+ /**
25
+ * Generate a secure, absolute URL to the given path.
26
+ *
27
+ * @param string $path
28
+ * @param array $parameters
29
+ * @return string
30
+ */
31
+ public function secure($path, $parameters = []);
32
+
33
+ /**
34
+ * Generate the URL to an application asset.
35
+ *
36
+ * @param string $path
37
+ * @param bool $secure
38
+ * @return string
39
+ */
40
+ public function asset($path, $secure = null);
41
+
42
+ /**
43
+ * Get the URL to a named route.
44
+ *
45
+ * @param string $name
46
+ * @param mixed $parameters
47
+ * @param bool $absolute
48
+ * @return string
49
+ *
50
+ * @throws \InvalidArgumentException
51
+ */
52
+ public function route($name, $parameters = [], $absolute = true);
53
+
54
+ /**
55
+ * Get the URL to a controller action.
56
+ *
57
+ * @param string $action
58
+ * @param mixed $parameters
59
+ * @param bool $absolute
60
+ * @return string
61
+ */
62
+ public function action($action, $parameters = [], $absolute = true);
63
+
64
+ /**
65
+ * Set the root controller namespace.
66
+ *
67
+ * @param string $rootNamespace
68
+ * @return $this
69
+ */
70
+ public function setRootControllerNamespace($rootNamespace);
71
+ }
vendor/illuminate/contracts/Routing/UrlRoutable.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Routing;
4
+
5
+ interface UrlRoutable
6
+ {
7
+ /**
8
+ * Get the value of the model's route key.
9
+ *
10
+ * @return mixed
11
+ */
12
+ public function getRouteKey();
13
+
14
+ /**
15
+ * Get the route key for the model.
16
+ *
17
+ * @return string
18
+ */
19
+ public function getRouteKeyName();
20
+ }
vendor/illuminate/contracts/Session/Session.php ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Session;
4
+
5
+ interface Session
6
+ {
7
+ /**
8
+ * Get the name of the session.
9
+ *
10
+ * @return string
11
+ */
12
+ public function getName();
13
+
14
+ /**
15
+ * Get the current session ID.
16
+ *
17
+ * @return string
18
+ */
19
+ public function getId();
20
+
21
+ /**
22
+ * Set the session ID.
23
+ *
24
+ * @param string $id
25
+ * @return void
26
+ */
27
+ public function setId($id);
28
+
29
+ /**
30
+ * Start the session, reading the data from a handler.
31
+ *
32
+ * @return bool
33
+ */
34
+ public function start();
35
+
36
+ /**
37
+ * Save the session data to storage.
38
+ *
39
+ * @return bool
40
+ */
41
+ public function save();
42
+
43
+ /**
44
+ * Get all of the session data.
45
+ *
46
+ * @return array
47
+ */
48
+ public function all();
49
+
50
+ /**
51
+ * Checks if a key exists.
52
+ *
53
+ * @param string|array $key
54
+ * @return bool
55
+ */
56
+ public function exists($key);
57
+
58
+ /**
59
+ * Checks if an a key is present and not null.
60
+ *
61
+ * @param string|array $key
62
+ * @return bool
63
+ */
64
+ public function has($key);
65
+
66
+ /**
67
+ * Get an item from the session.
68
+ *
69
+ * @param string $key
70
+ * @param mixed $default
71
+ * @return mixed
72
+ */
73
+ public function get($key, $default = null);
74
+
75
+ /**
76
+ * Put a key / value pair or array of key / value pairs in the session.
77
+ *
78
+ * @param string|array $key
79
+ * @param mixed $value
80
+ * @return void
81
+ */
82
+ public function put($key, $value = null);
83
+
84
+ /**
85
+ * Get the CSRF token value.
86
+ *
87
+ * @return string
88
+ */
89
+ public function token();
90
+
91
+ /**
92
+ * Remove an item from the session, returning its value.
93
+ *
94
+ * @param string $key
95
+ * @return mixed
96
+ */
97
+ public function remove($key);
98
+
99
+ /**
100
+ * Remove one or many items from the session.
101
+ *
102
+ * @param string|array $keys
103
+ * @return void
104
+ */
105
+ public function forget($keys);
106
+
107
+ /**
108
+ * Remove all of the items from the session.
109
+ *
110
+ * @return void
111
+ */
112
+ public function flush();
113
+
114
+ /**
115
+ * Generate a new session ID for the session.
116
+ *
117
+ * @param bool $destroy
118
+ * @return bool
119
+ */
120
+ public function migrate($destroy = false);
121
+
122
+ /**
123
+ * Determine if the session has been started.
124
+ *
125
+ * @return bool
126
+ */
127
+ public function isStarted();
128
+
129
+ /**
130
+ * Get the previous URL from the session.
131
+ *
132
+ * @return string|null
133
+ */
134
+ public function previousUrl();
135
+
136
+ /**
137
+ * Set the "previous" URL in the session.
138
+ *
139
+ * @param string $url
140
+ * @return void
141
+ */
142
+ public function setPreviousUrl($url);
143
+
144
+ /**
145
+ * Get the session handler instance.
146
+ *
147
+ * @return \SessionHandlerInterface
148
+ */
149
+ public function getHandler();
150
+
151
+ /**
152
+ * Determine if the session handler needs a request.
153
+ *
154
+ * @return bool
155
+ */
156
+ public function handlerNeedsRequest();
157
+
158
+ /**
159
+ * Set the request on the handler instance.
160
+ *
161
+ * @param \Illuminate\Http\Request $request
162
+ * @return void
163
+ */
164
+ public function setRequestOnHandler($request);
165
+ }
vendor/illuminate/contracts/Support/Arrayable.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Support;
4
+
5
+ interface Arrayable
6
+ {
7
+ /**
8
+ * Get the instance as an array.
9
+ *
10
+ * @return array
11
+ */
12
+ public function toArray();
13
+ }
vendor/illuminate/contracts/Support/Htmlable.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Support;
4
+
5
+ interface Htmlable
6
+ {
7
+ /**
8
+ * Get content as a string of HTML.
9
+ *
10
+ * @return string
11
+ */
12
+ public function toHtml();
13
+ }
vendor/illuminate/contracts/Support/Jsonable.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Support;
4
+
5
+ interface Jsonable
6
+ {
7
+ /**
8
+ * Convert the object to its JSON representation.
9
+ *
10
+ * @param int $options
11
+ * @return string
12
+ */
13
+ public function toJson($options = 0);
14
+ }
vendor/illuminate/contracts/Support/MessageBag.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Support;
4
+
5
+ interface MessageBag extends Arrayable
6
+ {
7
+ /**
8
+ * Get the keys present in the message bag.
9
+ *
10
+ * @return array
11
+ */
12
+ public function keys();
13
+
14
+ /**
15
+ * Add a message to the bag.
16
+ *
17
+ * @param string $key
18
+ * @param string $message
19
+ * @return $this
20
+ */
21
+ public function add($key, $message);
22
+
23
+ /**
24
+ * Merge a new array of messages into the bag.
25
+ *
26
+ * @param \Illuminate\Contracts\Support\MessageProvider|array $messages
27
+ * @return $this
28
+ */
29
+ public function merge($messages);
30
+
31
+ /**
32
+ * Determine if messages exist for a given key.
33
+ *
34
+ * @param string|array $key
35
+ * @return bool
36
+ */
37
+ public function has($key);
38
+
39
+ /**
40
+ * Get the first message from the bag for a given key.
41
+ *
42
+ * @param string $key
43
+ * @param string $format
44
+ * @return string
45
+ */
46
+ public function first($key = null, $format = null);
47
+
48
+ /**
49
+ * Get all of the messages from the bag for a given key.
50
+ *
51
+ * @param string $key
52
+ * @param string $format
53
+ * @return array
54
+ */
55
+ public function get($key, $format = null);
56
+
57
+ /**
58
+ * Get all of the messages for every key in the bag.
59
+ *
60
+ * @param string $format
61
+ * @return array
62
+ */
63
+ public function all($format = null);
64
+
65
+ /**
66
+ * Get the default message format.
67
+ *
68
+ * @return string
69
+ */
70
+ public function getFormat();
71
+
72
+ /**
73
+ * Set the default message format.
74
+ *
75
+ * @param string $format
76
+ * @return $this
77
+ */
78
+ public function setFormat($format = ':message');
79
+
80
+ /**
81
+ * Determine if the message bag has any messages.
82
+ *
83
+ * @return bool
84
+ */
85
+ public function isEmpty();
86
+
87
+ /**
88
+ * Get the number of messages in the container.
89
+ *
90
+ * @return int
91
+ */
92
+ public function count();
93
+ }
vendor/illuminate/contracts/Support/MessageProvider.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Support;
4
+
5
+ interface MessageProvider
6
+ {
7
+ /**
8
+ * Get the messages for the instance.
9
+ *
10
+ * @return \Illuminate\Contracts\Support\MessageBag
11
+ */
12
+ public function getMessageBag();
13
+ }
vendor/illuminate/contracts/Support/Renderable.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Support;
4
+
5
+ interface Renderable
6
+ {
7
+ /**
8
+ * Get the evaluated contents of the object.
9
+ *
10
+ * @return string
11
+ */
12
+ public function render();
13
+ }
vendor/illuminate/contracts/Translation/Translator.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Translation;
4
+
5
+ interface Translator
6
+ {
7
+ /**
8
+ * Get the translation for a given key.
9
+ *
10
+ * @param string $key
11
+ * @param array $replace
12
+ * @param string $locale
13
+ * @return mixed
14
+ */
15
+ public function trans($key, array $replace = [], $locale = null);
16
+
17
+ /**
18
+ * Get a translation according to an integer value.
19
+ *
20
+ * @param string $key
21
+ * @param int|array|\Countable $number
22
+ * @param array $replace
23
+ * @param string $locale
24
+ * @return string
25
+ */
26
+ public function transChoice($key, $number, array $replace = [], $locale = null);
27
+
28
+ /**
29
+ * Get the default locale being used.
30
+ *
31
+ * @return string
32
+ */
33
+ public function getLocale();
34
+
35
+ /**
36
+ * Set the default locale.
37
+ *
38
+ * @param string $locale
39
+ * @return void
40
+ */
41
+ public function setLocale($locale);
42
+ }
vendor/illuminate/contracts/Validation/Factory.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Validation;
4
+
5
+ interface Factory
6
+ {
7
+ /**
8
+ * Create a new Validator instance.
9
+ *
10
+ * @param array $data
11
+ * @param array $rules
12
+ * @param array $messages
13
+ * @param array $customAttributes
14
+ * @return \Illuminate\Contracts\Validation\Validator
15
+ */
16
+ public function make(array $data, array $rules, array $messages = [], array $customAttributes = []);
17
+
18
+ /**
19
+ * Register a custom validator extension.
20
+ *
21
+ * @param string $rule
22
+ * @param \Closure|string $extension
23
+ * @param string $message
24
+ * @return void
25
+ */
26
+ public function extend($rule, $extension, $message = null);
27
+
28
+ /**
29
+ * Register a custom implicit validator extension.
30
+ *
31
+ * @param string $rule
32
+ * @param \Closure|string $extension
33
+ * @param string $message
34
+ * @return void
35
+ */
36
+ public function extendImplicit($rule, $extension, $message = null);
37
+
38
+ /**
39
+ * Register a custom implicit validator message replacer.
40
+ *
41
+ * @param string $rule
42
+ * @param \Closure|string $replacer
43
+ * @return void
44
+ */
45
+ public function replacer($rule, $replacer);
46
+ }
vendor/illuminate/contracts/Validation/ValidatesWhenResolved.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Validation;
4
+
5
+ interface ValidatesWhenResolved
6
+ {
7
+ /**
8
+ * Validate the given class instance.
9
+ *
10
+ * @return void
11
+ */
12
+ public function validate();
13
+ }
vendor/illuminate/contracts/Validation/Validator.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\Validation;
4
+
5
+ use Illuminate\Contracts\Support\MessageProvider;
6
+
7
+ interface Validator extends MessageProvider
8
+ {
9
+ /**
10
+ * Determine if the data fails the validation rules.
11
+ *
12
+ * @return bool
13
+ */
14
+ public function fails();
15
+
16
+ /**
17
+ * Get the failed validation rules.
18
+ *
19
+ * @return array
20
+ */
21
+ public function failed();
22
+
23
+ /**
24
+ * Add conditions to a given field based on a Closure.
25
+ *
26
+ * @param string $attribute
27
+ * @param string|array $rules
28
+ * @param callable $callback
29
+ * @return $this
30
+ */
31
+ public function sometimes($attribute, $rules, callable $callback);
32
+
33
+ /**
34
+ * After an after validation callback.
35
+ *
36
+ * @param callable|string $callback
37
+ * @return $this
38
+ */
39
+ public function after($callback);
40
+ }
vendor/illuminate/contracts/View/Factory.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\View;
4
+
5
+ interface Factory
6
+ {
7
+ /**
8
+ * Determine if a given view exists.
9
+ *
10
+ * @param string $view
11
+ * @return bool
12
+ */
13
+ public function exists($view);
14
+
15
+ /**
16
+ * Get the evaluated view contents for the given path.
17
+ *
18
+ * @param string $path
19
+ * @param array $data
20
+ * @param array $mergeData
21
+ * @return \Illuminate\Contracts\View\View
22
+ */
23
+ public function file($path, $data = [], $mergeData = []);
24
+
25
+ /**
26
+ * Get the evaluated view contents for the given view.
27
+ *
28
+ * @param string $view
29
+ * @param array $data
30
+ * @param array $mergeData
31
+ * @return \Illuminate\Contracts\View\View
32
+ */
33
+ public function make($view, $data = [], $mergeData = []);
34
+
35
+ /**
36
+ * Add a piece of shared data to the environment.
37
+ *
38
+ * @param array|string $key
39
+ * @param mixed $value
40
+ * @return mixed
41
+ */
42
+ public function share($key, $value = null);
43
+
44
+ /**
45
+ * Register a view composer event.
46
+ *
47
+ * @param array|string $views
48
+ * @param \Closure|string $callback
49
+ * @return array
50
+ */
51
+ public function composer($views, $callback);
52
+
53
+ /**
54
+ * Register a view creator event.
55
+ *
56
+ * @param array|string $views
57
+ * @param \Closure|string $callback
58
+ * @return array
59
+ */
60
+ public function creator($views, $callback);
61
+
62
+ /**
63
+ * Add a new namespace to the loader.
64
+ *
65
+ * @param string $namespace
66
+ * @param string|array $hints
67
+ * @return $this
68
+ */
69
+ public function addNamespace($namespace, $hints);
70
+
71
+ /**
72
+ * Replace the namespace hints for the given namespace.
73
+ *
74
+ * @param string $namespace
75
+ * @param string|array $hints
76
+ * @return $this
77
+ */
78
+ public function replaceNamespace($namespace, $hints);
79
+ }
vendor/illuminate/contracts/View/View.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Contracts\View;
4
+
5
+ use Illuminate\Contracts\Support\Renderable;
6
+
7
+ interface View extends Renderable
8
+ {
9
+ /**
10
+ * Get the name of the view.
11
+ *
12
+ * @return string
13
+ */
14
+ public function name();
15
+
16
+ /**
17
+ * Add a piece of data to the view.
18
+ *
19
+ * @param string|array $key
20
+ * @param mixed $value
21
+ * @return $this
22
+ */
23
+ public function with($key, $value = null);
24
+ }
vendor/illuminate/contracts/composer.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "illuminate/contracts",
3
+ "description": "The Illuminate Contracts package.",
4
+ "license": "MIT",
5
+ "homepage": "https://laravel.com",
6
+ "support": {
7
+ "issues": "https://github.com/laravel/framework/issues",
8
+ "source": "https://github.com/laravel/framework"
9
+ },
10
+ "authors": [
11
+ {
12
+ "name": "Taylor Otwell",
13
+ "email": "taylor@laravel.com"
14
+ }
15
+ ],
16
+ "require": {
17
+ "php": ">=5.6.4"
18
+ },
19
+ "autoload": {
20
+ "psr-4": {
21
+ "Illuminate\\Contracts\\": ""
22
+ }
23
+ },
24
+ "extra": {
25
+ "branch-alias": {
26
+ "dev-master": "5.4-dev"
27
+ }
28
+ },
29
+ "config": {
30
+ "sort-packages": true
31
+ },
32
+ "minimum-stability": "dev"
33
+ }
vendor/illuminate/filesystem/Filesystem.php ADDED
@@ -0,0 +1,570 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Filesystem;
4
+
5
+ use ErrorException;
6
+ use FilesystemIterator;
7
+ use Symfony\Component\Finder\Finder;
8
+ use Illuminate\Support\Traits\Macroable;
9
+ use Illuminate\Contracts\Filesystem\FileNotFoundException;
10
+
11
+ class Filesystem
12
+ {
13
+ use Macroable;
14
+
15
+ /**
16
+ * Determine if a file or directory exists.
17
+ *
18
+ * @param string $path
19
+ * @return bool
20
+ */
21
+ public function exists($path)
22
+ {
23
+ return file_exists($path);
24
+ }
25
+
26
+ /**
27
+ * Get the contents of a file.
28
+ *
29
+ * @param string $path
30
+ * @param bool $lock
31
+ * @return string
32
+ *
33
+ * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
34
+ */
35
+ public function get($path, $lock = false)
36
+ {
37
+ if ($this->isFile($path)) {
38
+ return $lock ? $this->sharedGet($path) : file_get_contents($path);
39
+ }
40
+
41
+ throw new FileNotFoundException("File does not exist at path {$path}");
42
+ }
43
+
44
+ /**
45
+ * Get contents of a file with shared access.
46
+ *
47
+ * @param string $path
48
+ * @return string
49
+ */
50
+ public function sharedGet($path)
51
+ {
52
+ $contents = '';
53
+
54
+ $handle = fopen($path, 'rb');
55
+
56
+ if ($handle) {
57
+ try {
58
+ if (flock($handle, LOCK_SH)) {
59
+ clearstatcache(true, $path);
60
+
61
+ $contents = fread($handle, $this->size($path) ?: 1);
62
+
63
+ flock($handle, LOCK_UN);
64
+ }
65
+ } finally {
66
+ fclose($handle);
67
+ }
68
+ }
69
+
70
+ return $contents;
71
+ }
72
+
73
+ /**
74
+ * Get the returned value of a file.
75
+ *
76
+ * @param string $path
77
+ * @return mixed
78
+ *
79
+ * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
80
+ */
81
+ public function getRequire($path)
82
+ {
83
+ if ($this->isFile($path)) {
84
+ return require $path;
85
+ }
86
+
87
+ throw new FileNotFoundException("File does not exist at path {$path}");
88
+ }
89
+
90
+ /**
91
+ * Require the given file once.
92
+ *
93
+ * @param string $file
94
+ * @return mixed
95
+ */
96
+ public function requireOnce($file)
97
+ {
98
+ require_once $file;
99
+ }
100
+
101
+ /**
102
+ * Get the MD5 hash of the file at the given path.
103
+ *
104
+ * @param string $path
105
+ * @return string
106
+ */
107
+ public function hash($path)
108
+ {
109
+ return md5_file($path);
110
+ }
111
+
112
+ /**
113
+ * Write the contents of a file.
114
+ *
115
+ * @param string $path
116
+ * @param string $contents
117
+ * @param bool $lock
118
+ * @return int
119
+ */
120
+ public function put($path, $contents, $lock = false)
121
+ {
122
+ return file_put_contents($path, $contents, $lock ? LOCK_EX : 0);
123
+ }
124
+
125
+ /**
126
+ * Prepend to a file.
127
+ *
128
+ * @param string $path
129
+ * @param string $data
130
+ * @return int
131
+ */
132
+ public function prepend($path, $data)
133
+ {
134
+ if ($this->exists($path)) {
135
+ return $this->put($path, $data.$this->get($path));
136
+ }
137
+
138
+ return $this->put($path, $data);
139
+ }
140
+
141
+ /**
142
+ * Append to a file.
143
+ *
144
+ * @param string $path
145
+ * @param string $data
146
+ * @return int
147
+ */
148
+ public function append($path, $data)
149
+ {
150
+ return file_put_contents($path, $data, FILE_APPEND);
151
+ }
152
+
153
+ /**
154
+ * Get or set UNIX mode of a file or directory.
155
+ *
156
+ * @param string $path
157
+ * @param int $mode
158
+ * @return mixed
159
+ */
160
+ public function chmod($path, $mode = null)
161
+ {
162
+ if ($mode) {
163
+ return chmod($path, $mode);
164
+ }
165
+
166
+ return substr(sprintf('%o', fileperms($path)), -4);
167
+ }
168
+
169
+ /**
170
+ * Delete the file at a given path.
171
+ *
172
+ * @param string|array $paths
173
+ * @return bool
174
+ */
175
+ public function delete($paths)
176
+ {
177
+ $paths = is_array($paths) ? $paths : func_get_args();
178
+
179
+ $success = true;
180
+
181
+ foreach ($paths as $path) {
182
+ try {
183
+ if (! @unlink($path)) {
184
+ $success = false;
185
+ }
186
+ } catch (ErrorException $e) {
187
+ $success = false;
188
+ }
189
+ }
190
+
191
+ return $success;
192
+ }
193
+
194
+ /**
195
+ * Move a file to a new location.
196
+ *
197
+ * @param string $path
198
+ * @param string $target
199
+ * @return bool
200
+ */
201
+ public function move($path, $target)
202
+ {
203
+ return rename($path, $target);
204
+ }
205
+
206
+ /**
207
+ * Copy a file to a new location.
208
+ *
209
+ * @param string $path
210
+ * @param string $target
211
+ * @return bool
212
+ */
213
+ public function copy($path, $target)
214
+ {
215
+ return copy($path, $target);
216
+ }
217
+
218
+ /**
219
+ * Create a hard link to the target file or directory.
220
+ *
221
+ * @param string $target
222
+ * @param string $link
223
+ * @return void
224
+ */
225
+ public function link($target, $link)
226
+ {
227
+ if (! windows_os()) {
228
+ return symlink($target, $link);
229
+ }
230
+
231
+ $mode = $this->isDirectory($target) ? 'J' : 'H';
232
+
233
+ exec("mklink /{$mode} \"{$link}\" \"{$target}\"");
234
+ }
235
+
236
+ /**
237
+ * Extract the file name from a file path.
238
+ *
239
+ * @param string $path
240
+ * @return string
241
+ */
242
+ public function name($path)
243
+ {
244
+ return pathinfo($path, PATHINFO_FILENAME);
245
+ }
246
+
247
+ /**
248
+ * Extract the trailing name component from a file path.
249
+ *
250
+ * @param string $path
251
+ * @return string
252
+ */
253
+ public function basename($path)
254
+ {
255
+ return pathinfo($path, PATHINFO_BASENAME);
256
+ }
257
+
258
+ /**
259
+ * Extract the parent directory from a file path.
260
+ *
261
+ * @param string $path
262
+ * @return string
263
+ */
264
+ public function dirname($path)
265
+ {
266
+ return pathinfo($path, PATHINFO_DIRNAME);
267
+ }
268
+
269
+ /**
270
+ * Extract the file extension from a file path.
271
+ *
272
+ * @param string $path
273
+ * @return string
274
+ */
275
+ public function extension($path)
276
+ {
277
+ return pathinfo($path, PATHINFO_EXTENSION);
278
+ }
279
+
280
+ /**
281
+ * Get the file type of a given file.
282
+ *
283
+ * @param string $path
284
+ * @return string
285
+ */
286
+ public function type($path)
287
+ {
288
+ return filetype($path);
289
+ }
290
+
291
+ /**
292
+ * Get the mime-type of a given file.
293
+ *
294
+ * @param string $path
295
+ * @return string|false
296
+ */
297
+ public function mimeType($path)
298
+ {
299
+ return finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path);
300
+ }
301
+
302
+ /**
303
+ * Get the file size of a given file.
304
+ *
305
+ * @param string $path
306
+ * @return int
307
+ */
308
+ public function size($path)
309
+ {
310
+ return filesize($path);
311
+ }
312
+
313
+ /**
314
+ * Get the file's last modification time.
315
+ *
316
+ * @param string $path
317
+ * @return int
318
+ */
319
+ public function lastModified($path)
320
+ {
321
+ return filemtime($path);
322
+ }
323
+
324
+ /**
325
+ * Determine if the given path is a directory.
326
+ *
327
+ * @param string $directory
328
+ * @return bool
329
+ */
330
+ public function isDirectory($directory)
331
+ {
332
+ return is_dir($directory);
333
+ }
334
+
335
+ /**
336
+ * Determine if the given path is readable.
337
+ *
338
+ * @param string $path
339
+ * @return bool
340
+ */
341
+ public function isReadable($path)
342
+ {
343
+ return is_readable($path);
344
+ }
345
+
346
+ /**
347
+ * Determine if the given path is writable.
348
+ *
349
+ * @param string $path
350
+ * @return bool
351
+ */
352
+ public function isWritable($path)
353
+ {
354
+ return is_writable($path);
355
+ }
356
+
357
+ /**
358
+ * Determine if the given path is a file.
359
+ *
360
+ * @param string $file
361
+ * @return bool
362
+ */
363
+ public function isFile($file)
364
+ {
365
+ return is_file($file);
366
+ }
367
+
368
+ /**
369
+ * Find path names matching a given pattern.
370
+ *
371
+ * @param string $pattern
372
+ * @param int $flags
373
+ * @return array
374
+ */
375
+ public function glob($pattern, $flags = 0)
376
+ {
377
+ return glob($pattern, $flags);
378
+ }
379
+
380
+ /**
381
+ * Get an array of all files in a directory.
382
+ *
383
+ * @param string $directory
384
+ * @return array
385
+ */
386
+ public function files($directory)
387
+ {
388
+ $glob = glob($directory.DIRECTORY_SEPARATOR.'*');
389
+
390
+ if ($glob === false) {
391
+ return [];
392
+ }
393
+
394
+ // To get the appropriate files, we'll simply glob the directory and filter
395
+ // out any "files" that are not truly files so we do not end up with any
396
+ // directories in our list, but only true files within the directory.
397
+ return array_filter($glob, function ($file) {
398
+ return filetype($file) == 'file';
399
+ });
400
+ }
401
+
402
+ /**
403
+ * Get all of the files from the given directory (recursive).
404
+ *
405
+ * @param string $directory
406
+ * @param bool $hidden
407
+ * @return array
408
+ */
409
+ public function allFiles($directory, $hidden = false)
410
+ {
411
+ return iterator_to_array(Finder::create()->files()->ignoreDotFiles(! $hidden)->in($directory), false);
412
+ }
413
+
414
+ /**
415
+ * Get all of the directories within a given directory.
416
+ *
417
+ * @param string $directory
418
+ * @return array
419
+ */
420
+ public function directories($directory)
421
+ {
422
+ $directories = [];
423
+
424
+ foreach (Finder::create()->in($directory)->directories()->depth(0) as $dir) {
425
+ $directories[] = $dir->getPathname();
426
+ }
427
+
428
+ return $directories;
429
+ }
430
+
431
+ /**
432
+ * Create a directory.
433
+ *
434
+ * @param string $path
435
+ * @param int $mode
436
+ * @param bool $recursive
437
+ * @param bool $force
438
+ * @return bool
439
+ */
440
+ public function makeDirectory($path, $mode = 0755, $recursive = false, $force = false)
441
+ {
442
+ if ($force) {
443
+ return @mkdir($path, $mode, $recursive);
444
+ }
445
+
446
+ return mkdir($path, $mode, $recursive);
447
+ }
448
+
449
+ /**
450
+ * Move a directory.
451
+ *
452
+ * @param string $from
453
+ * @param string $to
454
+ * @param bool $overwrite
455
+ * @return bool
456
+ */
457
+ public function moveDirectory($from, $to, $overwrite = false)
458
+ {
459
+ if ($overwrite && $this->isDirectory($to)) {
460
+ if (! $this->deleteDirectory($to)) {
461
+ return false;
462
+ }
463
+ }
464
+
465
+ return @rename($from, $to) === true;
466
+ }
467
+
468
+ /**
469
+ * Copy a directory from one location to another.
470
+ *
471
+ * @param string $directory
472
+ * @param string $destination
473
+ * @param int $options
474
+ * @return bool
475
+ */
476
+ public function copyDirectory($directory, $destination, $options = null)
477
+ {
478
+ if (! $this->isDirectory($directory)) {
479
+ return false;
480
+ }
481
+
482
+ $options = $options ?: FilesystemIterator::SKIP_DOTS;
483
+
484
+ // If the destination directory does not actually exist, we will go ahead and
485
+ // create it recursively, which just gets the destination prepared to copy
486
+ // the files over. Once we make the directory we'll proceed the copying.
487
+ if (! $this->isDirectory($destination)) {
488
+ $this->makeDirectory($destination, 0777, true);
489
+ }
490
+
491
+ $items = new FilesystemIterator($directory, $options);
492
+
493
+ foreach ($items as $item) {
494
+ // As we spin through items, we will check to see if the current file is actually
495
+ // a directory or a file. When it is actually a directory we will need to call
496
+ // back into this function recursively to keep copying these nested folders.
497
+ $target = $destination.'/'.$item->getBasename();
498
+
499
+ if ($item->isDir()) {
500
+ $path = $item->getPathname();
501
+
502
+ if (! $this->copyDirectory($path, $target, $options)) {
503
+ return false;
504
+ }
505
+ }
506
+
507
+ // If the current items is just a regular file, we will just copy this to the new
508
+ // location and keep looping. If for some reason the copy fails we'll bail out
509
+ // and return false, so the developer is aware that the copy process failed.
510
+ else {
511
+ if (! $this->copy($item->getPathname(), $target)) {
512
+ return false;
513
+ }
514
+ }
515
+ }
516
+
517
+ return true;
518
+ }
519
+
520
+ /**
521
+ * Recursively delete a directory.
522
+ *
523
+ * The directory itself may be optionally preserved.
524
+ *
525
+ * @param string $directory
526
+ * @param bool $preserve
527
+ * @return bool
528
+ */
529
+ public function deleteDirectory($directory, $preserve = false)
530
+ {
531
+ if (! $this->isDirectory($directory)) {
532
+ return false;
533
+ }
534
+
535
+ $items = new FilesystemIterator($directory);
536
+
537
+ foreach ($items as $item) {
538
+ // If the item is a directory, we can just recurse into the function and
539
+ // delete that sub-directory otherwise we'll just delete the file and
540
+ // keep iterating through each file until the directory is cleaned.
541
+ if ($item->isDir() && ! $item->isLink()) {
542
+ $this->deleteDirectory($item->getPathname());
543
+ }
544
+
545
+ // If the item is just a file, we can go ahead and delete it since we're
546
+ // just looping through and waxing all of the files in this directory
547
+ // and calling directories recursively, so we delete the real path.
548
+ else {
549
+ $this->delete($item->getPathname());
550
+ }
551
+ }
552
+
553
+ if (! $preserve) {
554
+ @rmdir($directory);
555
+ }
556
+
557
+ return true;
558
+ }
559
+
560
+ /**
561
+ * Empty the specified directory of all files and folders.
562
+ *
563
+ * @param string $directory
564
+ * @return bool
565
+ */
566
+ public function cleanDirectory($directory)
567
+ {
568
+ return $this->deleteDirectory($directory, true);
569
+ }
570
+ }
vendor/illuminate/filesystem/FilesystemAdapter.php ADDED
@@ -0,0 +1,548 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Filesystem;
4
+
5
+ use RuntimeException;
6
+ use Illuminate\Http\File;
7
+ use Illuminate\Support\Str;
8
+ use InvalidArgumentException;
9
+ use Illuminate\Http\UploadedFile;
10
+ use Illuminate\Support\Collection;
11
+ use League\Flysystem\AdapterInterface;
12
+ use PHPUnit\Framework\Assert as PHPUnit;
13
+ use League\Flysystem\FilesystemInterface;
14
+ use League\Flysystem\AwsS3v3\AwsS3Adapter;
15
+ use League\Flysystem\FileNotFoundException;
16
+ use League\Flysystem\Adapter\Local as LocalAdapter;
17
+ use Illuminate\Contracts\Filesystem\Cloud as CloudFilesystemContract;
18
+ use Illuminate\Contracts\Filesystem\Filesystem as FilesystemContract;
19
+ use Illuminate\Contracts\Filesystem\FileNotFoundException as ContractFileNotFoundException;
20
+
21
+ /**
22
+ * @mixin \League\Flysystem\FilesystemInterface
23
+ */
24
+ class FilesystemAdapter implements FilesystemContract, CloudFilesystemContract
25
+ {
26
+ /**
27
+ * The Flysystem filesystem implementation.
28
+ *
29
+ * @var \League\Flysystem\FilesystemInterface
30
+ */
31
+ protected $driver;
32
+
33
+ /**
34
+ * Create a new filesystem adapter instance.
35
+ *
36
+ * @param \League\Flysystem\FilesystemInterface $driver
37
+ * @return void
38
+ */
39
+ public function __construct(FilesystemInterface $driver)
40
+ {
41
+ $this->driver = $driver;
42
+ }
43
+
44
+ /**
45
+ * Assert that the given file exists.
46
+ *
47
+ * @param string $path
48
+ * @return void
49
+ */
50
+ public function assertExists($path)
51
+ {
52
+ PHPUnit::assertTrue(
53
+ $this->exists($path), "Unable to find a file at path [{$path}]."
54
+ );
55
+ }
56
+
57
+ /**
58
+ * Assert that the given file does not exist.
59
+ *
60
+ * @param string $path
61
+ * @return void
62
+ */
63
+ public function assertMissing($path)
64
+ {
65
+ PHPUnit::assertFalse(
66
+ $this->exists($path), "Found unexpected file at path [{$path}]."
67
+ );
68
+ }
69
+
70
+ /**
71
+ * Determine if a file exists.
72
+ *
73
+ * @param string $path
74
+ * @return bool
75
+ */
76
+ public function exists($path)
77
+ {
78
+ return $this->driver->has($path);
79
+ }
80
+
81
+ /**
82
+ * Get the full path for the file at the given "short" path.
83
+ *
84
+ * @param string $path
85
+ * @return string
86
+ */
87
+ public function path($path)
88
+ {
89
+ return $this->driver->getAdapter()->getPathPrefix().$path;
90
+ }
91
+
92
+ /**
93
+ * Get the contents of a file.
94
+ *
95
+ * @param string $path
96
+ * @return string
97
+ *
98
+ * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
99
+ */
100
+ public function get($path)
101
+ {
102
+ try {
103
+ return $this->driver->read($path);
104
+ } catch (FileNotFoundException $e) {
105
+ throw new ContractFileNotFoundException($path, $e->getCode(), $e);
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Write the contents of a file.
111
+ *
112
+ * @param string $path
113
+ * @param string|resource $contents
114
+ * @param array $options
115
+ * @return bool
116
+ */
117
+ public function put($path, $contents, $options = [])
118
+ {
119
+ if (is_string($options)) {
120
+ $options = ['visibility' => $options];
121
+ }
122
+
123
+ // If the given contents is actually a file or uploaded file instance than we will
124
+ // automatically store the file using a stream. This provides a convenient path
125
+ // for the developer to store streams without managing them manually in code.
126
+ if ($contents instanceof File ||
127
+ $contents instanceof UploadedFile) {
128
+ return $this->putFile($path, $contents, $options);
129
+ }
130
+
131
+ return is_resource($contents)
132
+ ? $this->driver->putStream($path, $contents, $options)
133
+ : $this->driver->put($path, $contents, $options);
134
+ }
135
+
136
+ /**
137
+ * Store the uploaded file on the disk.
138
+ *
139
+ * @param string $path
140
+ * @param \Illuminate\Http\File|\Illuminate\Http\UploadedFile $file
141
+ * @param array $options
142
+ * @return string|false
143
+ */
144
+ public function putFile($path, $file, $options = [])
145
+ {
146
+ return $this->putFileAs($path, $file, $file->hashName(), $options);
147
+ }
148
+
149
+ /**
150
+ * Store the uploaded file on the disk with a given name.
151
+ *
152
+ * @param string $path
153
+ * @param \Illuminate\Http\File|\Illuminate\Http\UploadedFile $file
154
+ * @param string $name
155
+ * @param array $options
156
+ * @return string|false
157
+ */
158
+ public function putFileAs($path, $file, $name, $options = [])
159
+ {
160
+ $stream = fopen($file->getRealPath(), 'r+');
161
+
162
+ // Next, we will format the path of the file and store the file using a stream since
163
+ // they provide better performance than alternatives. Once we write the file this
164
+ // stream will get closed automatically by us so the developer doesn't have to.
165
+ $result = $this->put(
166
+ $path = trim($path.'/'.$name, '/'), $stream, $options
167
+ );
168
+
169
+ if (is_resource($stream)) {
170
+ fclose($stream);
171
+ }
172
+
173
+ return $result ? $path : false;
174
+ }
175
+
176
+ /**
177
+ * Get the visibility for the given path.
178
+ *
179
+ * @param string $path
180
+ * @return string
181
+ */
182
+ public function getVisibility($path)
183
+ {
184
+ if ($this->driver->getVisibility($path) == AdapterInterface::VISIBILITY_PUBLIC) {
185
+ return FilesystemContract::VISIBILITY_PUBLIC;
186
+ }
187
+
188
+ return FilesystemContract::VISIBILITY_PRIVATE;
189
+ }
190
+
191
+ /**
192
+ * Set the visibility for the given path.
193
+ *
194
+ * @param string $path
195
+ * @param string $visibility
196
+ * @return void
197
+ */
198
+ public function setVisibility($path, $visibility)
199
+ {
200
+ return $this->driver->setVisibility($path, $this->parseVisibility($visibility));
201
+ }
202
+
203
+ /**
204
+ * Prepend to a file.
205
+ *
206
+ * @param string $path
207
+ * @param string $data
208
+ * @param string $separator
209
+ * @return int
210
+ */
211
+ public function prepend($path, $data, $separator = PHP_EOL)
212
+ {
213
+ if ($this->exists($path)) {
214
+ return $this->put($path, $data.$separator.$this->get($path));
215
+ }
216
+
217
+ return $this->put($path, $data);
218
+ }
219
+
220
+ /**
221
+ * Append to a file.
222
+ *
223
+ * @param string $path
224
+ * @param string $data
225
+ * @param string $separator
226
+ * @return int
227
+ */
228
+ public function append($path, $data, $separator = PHP_EOL)
229
+ {
230
+ if ($this->exists($path)) {
231
+ return $this->put($path, $this->get($path).$separator.$data);
232
+ }
233
+
234
+ return $this->put($path, $data);
235
+ }
236
+
237
+ /**
238
+ * Delete the file at a given path.
239
+ *
240
+ * @param string|array $paths
241
+ * @return bool
242
+ */
243
+ public function delete($paths)
244
+ {
245
+ $paths = is_array($paths) ? $paths : func_get_args();
246
+
247
+ $success = true;
248
+
249
+ foreach ($paths as $path) {
250
+ try {
251
+ if (! $this->driver->delete($path)) {
252
+ $success = false;
253
+ }
254
+ } catch (FileNotFoundException $e) {
255
+ $success = false;
256
+ }
257
+ }
258
+
259
+ return $success;
260
+ }
261
+
262
+ /**
263
+ * Copy a file to a new location.
264
+ *
265
+ * @param string $from
266
+ * @param string $to
267
+ * @return bool
268
+ */
269
+ public function copy($from, $to)
270
+ {
271
+ return $this->driver->copy($from, $to);
272
+ }
273
+
274
+ /**
275
+ * Move a file to a new location.
276
+ *
277
+ * @param string $from
278
+ * @param string $to
279
+ * @return bool
280
+ */
281
+ public function move($from, $to)
282
+ {
283
+ return $this->driver->rename($from, $to);
284
+ }
285
+
286
+ /**
287
+ * Get the file size of a given file.
288
+ *
289
+ * @param string $path
290
+ * @return int
291
+ */
292
+ public function size($path)
293
+ {
294
+ return $this->driver->getSize($path);
295
+ }
296
+
297
+ /**
298
+ * Get the mime-type of a given file.
299
+ *
300
+ * @param string $path
301
+ * @return string|false
302
+ */
303
+ public function mimeType($path)
304
+ {
305
+ return $this->driver->getMimetype($path);
306
+ }
307
+
308
+ /**
309
+ * Get the file's last modification time.
310
+ *
311
+ * @param string $path
312
+ * @return int
313
+ */
314
+ public function lastModified($path)
315
+ {
316
+ return $this->driver->getTimestamp($path);
317
+ }
318
+
319
+ /**
320
+ * Get the URL for the file at the given path.
321
+ *
322
+ * @param string $path
323
+ * @return string
324
+ */
325
+ public function url($path)
326
+ {
327
+ $adapter = $this->driver->getAdapter();
328
+
329
+ if (method_exists($adapter, 'getUrl')) {
330
+ return $adapter->getUrl($path);
331
+ } elseif ($adapter instanceof AwsS3Adapter) {
332
+ return $this->getAwsUrl($adapter, $path);
333
+ } elseif ($adapter instanceof LocalAdapter) {
334
+ return $this->getLocalUrl($path);
335
+ } else {
336
+ throw new RuntimeException('This driver does not support retrieving URLs.');
337
+ }
338
+ }
339
+
340
+ /**
341
+ * Get the URL for the file at the given path.
342
+ *
343
+ * @param \League\Flysystem\AwsS3v3\AwsS3Adapter $adapter
344
+ * @param string $path
345
+ * @return string
346
+ */
347
+ protected function getAwsUrl($adapter, $path)
348
+ {
349
+ return $adapter->getClient()->getObjectUrl(
350
+ $adapter->getBucket(), $adapter->getPathPrefix().$path
351
+ );
352
+ }
353
+
354
+ /**
355
+ * Get the URL for the file at the given path.
356
+ *
357
+ * @param string $path
358
+ * @return string
359
+ */
360
+ protected function getLocalUrl($path)
361
+ {
362
+ $config = $this->driver->getConfig();
363
+
364
+ // If an explicit base URL has been set on the disk configuration then we will use
365
+ // it as the base URL instead of the default path. This allows the developer to
366
+ // have full control over the base path for this filesystem's generated URLs.
367
+ if ($config->has('url')) {
368
+ return rtrim($config->get('url'), '/').'/'.ltrim($path, '/');
369
+ }
370
+
371
+ $path = '/storage/'.$path;
372
+
373
+ // If the path contains "storage/public", it probably means the developer is using
374
+ // the default disk to generate the path instead of the "public" disk like they
375
+ // are really supposed to use. We will remove the public from this path here.
376
+ if (Str::contains($path, '/storage/public/')) {
377
+ return Str::replaceFirst('/public/', '/', $path);
378
+ } else {
379
+ return $path;
380
+ }
381
+ }
382
+
383
+ /**
384
+ * Get a temporary URL for the file at the given path.
385
+ *
386
+ * @param string $path
387
+ * @param \DateTimeInterface $expiration
388
+ * @param array $options
389
+ * @return string
390
+ */
391
+ public function temporaryUrl($path, $expiration, array $options = [])
392
+ {
393
+ $adapter = $this->driver->getAdapter();
394
+
395
+ if (method_exists($adapter, 'getTemporaryUrl')) {
396
+ return $adapter->getTemporaryUrl($path, $expiration, $options);
397
+ } elseif (! $adapter instanceof AwsS3Adapter) {
398
+ throw new RuntimeException('This driver does not support creating temporary URLs.');
399
+ }
400
+
401
+ $client = $adapter->getClient();
402
+
403
+ $command = $client->getCommand('GetObject', array_merge([
404
+ 'Bucket' => $adapter->getBucket(),
405
+ 'Key' => $adapter->getPathPrefix().$path,
406
+ ], $options));
407
+
408
+ return (string) $client->createPresignedRequest(
409
+ $command, $expiration
410
+ )->getUri();
411
+ }
412
+
413
+ /**
414
+ * Get an array of all files in a directory.
415
+ *
416
+ * @param string|null $directory
417
+ * @param bool $recursive
418
+ * @return array
419
+ */
420
+ public function files($directory = null, $recursive = false)
421
+ {
422
+ $contents = $this->driver->listContents($directory, $recursive);
423
+
424
+ return $this->filterContentsByType($contents, 'file');
425
+ }
426
+
427
+ /**
428
+ * Get all of the files from the given directory (recursive).
429
+ *
430
+ * @param string|null $directory
431
+ * @return array
432
+ */
433
+ public function allFiles($directory = null)
434
+ {
435
+ return $this->files($directory, true);
436
+ }
437
+
438
+ /**
439
+ * Get all of the directories within a given directory.
440
+ *
441
+ * @param string|null $directory
442
+ * @param bool $recursive
443
+ * @return array
444
+ */
445
+ public function directories($directory = null, $recursive = false)
446
+ {
447
+ $contents = $this->driver->listContents($directory, $recursive);
448
+
449
+ return $this->filterContentsByType($contents, 'dir');
450
+ }
451
+
452
+ /**
453
+ * Get all (recursive) of the directories within a given directory.
454
+ *
455
+ * @param string|null $directory
456
+ * @return array
457
+ */
458
+ public function allDirectories($directory = null)
459
+ {
460
+ return $this->directories($directory, true);
461
+ }
462
+
463
+ /**
464
+ * Create a directory.
465
+ *
466
+ * @param string $path
467
+ * @return bool
468
+ */
469
+ public function makeDirectory($path)
470
+ {
471
+ return $this->driver->createDir($path);
472
+ }
473
+
474
+ /**
475
+ * Recursively delete a directory.
476
+ *
477
+ * @param string $directory
478
+ * @return bool
479
+ */
480
+ public function deleteDirectory($directory)
481
+ {
482
+ return $this->driver->deleteDir($directory);
483
+ }
484
+
485
+ /**
486
+ * Get the Flysystem driver.
487
+ *
488
+ * @return \League\Flysystem\FilesystemInterface
489
+ */
490
+ public function getDriver()
491
+ {
492
+ return $this->driver;
493
+ }
494
+
495
+ /**
496
+ * Filter directory contents by type.
497
+ *
498
+ * @param array $contents
499
+ * @param string $type
500
+ * @return array
501
+ */
502
+ protected function filterContentsByType($contents, $type)
503
+ {
504
+ return Collection::make($contents)
505
+ ->where('type', $type)
506
+ ->pluck('path')
507
+ ->values()
508
+ ->all();
509
+ }
510
+
511
+ /**
512
+ * Parse the given visibility value.
513
+ *
514
+ * @param string|null $visibility
515
+ * @return string|null
516
+ *
517
+ * @throws \InvalidArgumentException
518
+ */
519
+ protected function parseVisibility($visibility)
520
+ {
521
+ if (is_null($visibility)) {
522
+ return;
523
+ }
524
+
525
+ switch ($visibility) {
526
+ case FilesystemContract::VISIBILITY_PUBLIC:
527
+ return AdapterInterface::VISIBILITY_PUBLIC;
528
+ case FilesystemContract::VISIBILITY_PRIVATE:
529
+ return AdapterInterface::VISIBILITY_PRIVATE;
530
+ }
531
+
532
+ throw new InvalidArgumentException('Unknown visibility: '.$visibility);
533
+ }
534
+
535
+ /**
536
+ * Pass dynamic methods call onto Flysystem.
537
+ *
538
+ * @param string $method
539
+ * @param array $parameters
540
+ * @return mixed
541
+ *
542
+ * @throws \BadMethodCallException
543
+ */
544
+ public function __call($method, array $parameters)
545
+ {
546
+ return call_user_func_array([$this->driver, $method], $parameters);
547
+ }
548
+ }
vendor/illuminate/filesystem/FilesystemManager.php ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Filesystem;
4
+
5
+ use Closure;
6
+ use Aws\S3\S3Client;
7
+ use OpenCloud\Rackspace;
8
+ use Illuminate\Support\Arr;
9
+ use InvalidArgumentException;
10
+ use League\Flysystem\AdapterInterface;
11
+ use League\Flysystem\FilesystemInterface;
12
+ use League\Flysystem\Filesystem as Flysystem;
13
+ use League\Flysystem\Adapter\Ftp as FtpAdapter;
14
+ use League\Flysystem\Rackspace\RackspaceAdapter;
15
+ use League\Flysystem\Adapter\Local as LocalAdapter;
16
+ use League\Flysystem\AwsS3v3\AwsS3Adapter as S3Adapter;
17
+ use Illuminate\Contracts\Filesystem\Factory as FactoryContract;
18
+
19
+ /**
20
+ * @mixin \Illuminate\Contracts\Filesystem\Filesystem
21
+ */
22
+ class FilesystemManager implements FactoryContract
23
+ {
24
+ /**
25
+ * The application instance.
26
+ *
27
+ * @var \Illuminate\Contracts\Foundation\Application
28
+ */
29
+ protected $app;
30
+
31
+ /**
32
+ * The array of resolved filesystem drivers.
33
+ *
34
+ * @var array
35
+ */
36
+ protected $disks = [];
37
+
38
+ /**
39
+ * The registered custom driver creators.
40
+ *
41
+ * @var array
42
+ */
43
+ protected $customCreators = [];
44
+
45
+ /**
46
+ * Create a new filesystem manager instance.
47
+ *
48
+ * @param \Illuminate\Contracts\Foundation\Application $app
49
+ * @return void
50
+ */
51
+ public function __construct($app)
52
+ {
53
+ $this->app = $app;
54
+ }
55
+
56
+ /**
57
+ * Get a filesystem instance.
58
+ *
59
+ * @param string $name
60
+ * @return \Illuminate\Contracts\Filesystem\Filesystem
61
+ */
62
+ public function drive($name = null)
63
+ {
64
+ return $this->disk($name);
65
+ }
66
+
67
+ /**
68
+ * Get a filesystem instance.
69
+ *
70
+ * @param string $name
71
+ * @return \Illuminate\Contracts\Filesystem\Filesystem
72
+ */
73
+ public function disk($name = null)
74
+ {
75
+ $name = $name ?: $this->getDefaultDriver();
76
+
77
+ return $this->disks[$name] = $this->get($name);
78
+ }
79
+
80
+ /**
81
+ * Get a default cloud filesystem instance.
82
+ *
83
+ * @return \Illuminate\Contracts\Filesystem\Filesystem
84
+ */
85
+ public function cloud()
86
+ {
87
+ $name = $this->getDefaultCloudDriver();
88
+
89
+ return $this->disks[$name] = $this->get($name);
90
+ }
91
+
92
+ /**
93
+ * Attempt to get the disk from the local cache.
94
+ *
95
+ * @param string $name
96
+ * @return \Illuminate\Contracts\Filesystem\Filesystem
97
+ */
98
+ protected function get($name)
99
+ {
100
+ return isset($this->disks[$name]) ? $this->disks[$name] : $this->resolve($name);
101
+ }
102
+
103
+ /**
104
+ * Resolve the given disk.
105
+ *
106
+ * @param string $name
107
+ * @return \Illuminate\Contracts\Filesystem\Filesystem
108
+ *
109
+ * @throws \InvalidArgumentException
110
+ */
111
+ protected function resolve($name)
112
+ {
113
+ $config = $this->getConfig($name);
114
+
115
+ if (isset($this->customCreators[$config['driver']])) {
116
+ return $this->callCustomCreator($config);
117
+ }
118
+
119
+ $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
120
+
121
+ if (method_exists($this, $driverMethod)) {
122
+ return $this->{$driverMethod}($config);
123
+ } else {
124
+ throw new InvalidArgumentException("Driver [{$config['driver']}] is not supported.");
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Call a custom driver creator.
130
+ *
131
+ * @param array $config
132
+ * @return \Illuminate\Contracts\Filesystem\Filesystem
133
+ */
134
+ protected function callCustomCreator(array $config)
135
+ {
136
+ $driver = $this->customCreators[$config['driver']]($this->app, $config);
137
+
138
+ if ($driver instanceof FilesystemInterface) {
139
+ return $this->adapt($driver);
140
+ }
141
+
142
+ return $driver;
143
+ }
144
+
145
+ /**
146
+ * Create an instance of the local driver.
147
+ *
148
+ * @param array $config
149
+ * @return \Illuminate\Contracts\Filesystem\Filesystem
150
+ */
151
+ public function createLocalDriver(array $config)
152
+ {
153
+ $permissions = isset($config['permissions']) ? $config['permissions'] : [];
154
+
155
+ $links = Arr::get($config, 'links') === 'skip'
156
+ ? LocalAdapter::SKIP_LINKS
157
+ : LocalAdapter::DISALLOW_LINKS;
158
+
159
+ return $this->adapt($this->createFlysystem(new LocalAdapter(
160
+ $config['root'], LOCK_EX, $links, $permissions
161
+ ), $config));
162
+ }
163
+
164
+ /**
165
+ * Create an instance of the ftp driver.
166
+ *
167
+ * @param array $config
168
+ * @return \Illuminate\Contracts\Filesystem\Filesystem
169
+ */
170
+ public function createFtpDriver(array $config)
171
+ {
172
+ $ftpConfig = Arr::only($config, [
173
+ 'host', 'username', 'password', 'port', 'root', 'passive', 'ssl', 'timeout',
174
+ ]);
175
+
176
+ return $this->adapt($this->createFlysystem(
177
+ new FtpAdapter($ftpConfig), $config
178
+ ));
179
+ }
180
+
181
+ /**
182
+ * Create an instance of the Amazon S3 driver.
183
+ *
184
+ * @param array $config
185
+ * @return \Illuminate\Contracts\Filesystem\Cloud
186
+ */
187
+ public function createS3Driver(array $config)
188
+ {
189
+ $s3Config = $this->formatS3Config($config);
190
+
191
+ $root = isset($s3Config['root']) ? $s3Config['root'] : null;
192
+
193
+ $options = isset($config['options']) ? $config['options'] : [];
194
+
195
+ return $this->adapt($this->createFlysystem(
196
+ new S3Adapter(new S3Client($s3Config), $s3Config['bucket'], $root, $options), $config
197
+ ));
198
+ }
199
+
200
+ /**
201
+ * Format the given S3 configuration with the default options.
202
+ *
203
+ * @param array $config
204
+ * @return array
205
+ */
206
+ protected function formatS3Config(array $config)
207
+ {
208
+ $config += ['version' => 'latest'];
209
+
210
+ if ($config['key'] && $config['secret']) {
211
+ $config['credentials'] = Arr::only($config, ['key', 'secret']);
212
+ }
213
+
214
+ return $config;
215
+ }
216
+
217
+ /**
218
+ * Create an instance of the Rackspace driver.
219
+ *
220
+ * @param array $config
221
+ * @return \Illuminate\Contracts\Filesystem\Cloud
222
+ */
223
+ public function createRackspaceDriver(array $config)
224
+ {
225
+ $client = new Rackspace($config['endpoint'], [
226
+ 'username' => $config['username'], 'apiKey' => $config['key'],
227
+ ]);
228
+
229
+ $root = isset($config['root']) ? $config['root'] : null;
230
+
231
+ return $this->adapt($this->createFlysystem(
232
+ new RackspaceAdapter($this->getRackspaceContainer($client, $config), $root), $config
233
+ ));
234
+ }
235
+
236
+ /**
237
+ * Get the Rackspace Cloud Files container.
238
+ *
239
+ * @param \OpenCloud\Rackspace $client
240
+ * @param array $config
241
+ * @return \OpenCloud\ObjectStore\Resource\Container
242
+ */
243
+ protected function getRackspaceContainer(Rackspace $client, array $config)
244
+ {
245
+ $urlType = Arr::get($config, 'url_type');
246
+
247
+ $store = $client->objectStoreService('cloudFiles', $config['region'], $urlType);
248
+
249
+ return $store->getContainer($config['container']);
250
+ }
251
+
252
+ /**
253
+ * Create a Flysystem instance with the given adapter.
254
+ *
255
+ * @param \League\Flysystem\AdapterInterface $adapter
256
+ * @param array $config
257
+ * @return \League\Flysystem\FlysystemInterface
258
+ */
259
+ protected function createFlysystem(AdapterInterface $adapter, array $config)
260
+ {
261
+ $config = Arr::only($config, ['visibility', 'disable_asserts', 'url']);
262
+
263
+ return new Flysystem($adapter, count($config) > 0 ? $config : null);
264
+ }
265
+
266
+ /**
267
+ * Adapt the filesystem implementation.
268
+ *
269
+ * @param \League\Flysystem\FilesystemInterface $filesystem
270
+ * @return \Illuminate\Contracts\Filesystem\Filesystem
271
+ */
272
+ protected function adapt(FilesystemInterface $filesystem)
273
+ {
274
+ return new FilesystemAdapter($filesystem);
275
+ }
276
+
277
+ /**
278
+ * Set the given disk instance.
279
+ *
280
+ * @param string $name
281
+ * @param mixed $disk
282
+ * @return void
283
+ */
284
+ public function set($name, $disk)
285
+ {
286
+ $this->disks[$name] = $disk;
287
+ }
288
+
289
+ /**
290
+ * Get the filesystem connection configuration.
291
+ *
292
+ * @param string $name
293
+ * @return array
294
+ */
295
+ protected function getConfig($name)
296
+ {
297
+ return $this->app['config']["filesystems.disks.{$name}"];
298
+ }
299
+
300
+ /**
301
+ * Get the default driver name.
302
+ *
303
+ * @return string
304
+ */
305
+ public function getDefaultDriver()
306
+ {
307
+ return $this->app['config']['filesystems.default'];
308
+ }
309
+
310
+ /**
311
+ * Get the default cloud driver name.
312
+ *
313
+ * @return string
314
+ */
315
+ public function getDefaultCloudDriver()
316
+ {
317
+ return $this->app['config']['filesystems.cloud'];
318
+ }
319
+
320
+ /**
321
+ * Register a custom driver creator Closure.
322
+ *
323
+ * @param string $driver
324
+ * @param \Closure $callback
325
+ * @return $this
326
+ */
327
+ public function extend($driver, Closure $callback)
328
+ {
329
+ $this->customCreators[$driver] = $callback;
330
+
331
+ return $this;
332
+ }
333
+
334
+ /**
335
+ * Dynamically call the default driver instance.
336
+ *
337
+ * @param string $method
338
+ * @param array $parameters
339
+ * @return mixed
340
+ */
341
+ public function __call($method, $parameters)
342
+ {
343
+ return $this->disk()->$method(...$parameters);
344
+ }
345
+ }
vendor/illuminate/filesystem/FilesystemServiceProvider.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Filesystem;
4
+
5
+ use Illuminate\Support\ServiceProvider;
6
+
7
+ class FilesystemServiceProvider extends ServiceProvider
8
+ {
9
+ /**
10
+ * Register the service provider.
11
+ *
12
+ * @return void
13
+ */
14
+ public function register()
15
+ {
16
+ $this->registerNativeFilesystem();
17
+
18
+ $this->registerFlysystem();
19
+ }
20
+
21
+ /**
22
+ * Register the native filesystem implementation.
23
+ *
24
+ * @return void
25
+ */
26
+ protected function registerNativeFilesystem()
27
+ {
28
+ $this->app->singleton('files', function () {
29
+ return new Filesystem;
30
+ });
31
+ }
32
+
33
+ /**
34
+ * Register the driver based filesystem.
35
+ *
36
+ * @return void
37
+ */
38
+ protected function registerFlysystem()
39
+ {
40
+ $this->registerManager();
41
+
42
+ $this->app->singleton('filesystem.disk', function () {
43
+ return $this->app['filesystem']->disk($this->getDefaultDriver());
44
+ });
45
+
46
+ $this->app->singleton('filesystem.cloud', function () {
47
+ return $this->app['filesystem']->disk($this->getCloudDriver());
48
+ });
49
+ }
50
+
51
+ /**
52
+ * Register the filesystem manager.
53
+ *
54
+ * @return void
55
+ */
56
+ protected function registerManager()
57
+ {
58
+ $this->app->singleton('filesystem', function () {
59
+ return new FilesystemManager($this->app);
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Get the default file driver.
65
+ *
66
+ * @return string
67
+ */
68
+ protected function getDefaultDriver()
69
+ {
70
+ return $this->app['config']['filesystems.default'];
71
+ }
72
+
73
+ /**
74
+ * Get the default cloud based file driver.
75
+ *
76
+ * @return string
77
+ */
78
+ protected function getCloudDriver()
79
+ {
80
+ return $this->app['config']['filesystems.cloud'];
81
+ }
82
+ }
vendor/illuminate/filesystem/composer.json ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "illuminate/filesystem",
3
+ "description": "The Illuminate Filesystem package.",
4
+ "license": "MIT",
5
+ "homepage": "https://laravel.com",
6
+ "support": {
7
+ "issues": "https://github.com/laravel/framework/issues",
8
+ "source": "https://github.com/laravel/framework"
9
+ },
10
+ "authors": [
11
+ {
12
+ "name": "Taylor Otwell",
13
+ "email": "taylor@laravel.com"
14
+ }
15
+ ],
16
+ "require": {
17
+ "php": ">=5.6.4",
18
+ "illuminate/contracts": "5.4.*",
19
+ "illuminate/support": "5.4.*",
20
+ "symfony/finder": "~3.2"
21
+ },
22
+ "autoload": {
23
+ "psr-4": {
24
+ "Illuminate\\Filesystem\\": ""
25
+ }
26
+ },
27
+ "extra": {
28
+ "branch-alias": {
29
+ "dev-master": "5.4-dev"
30
+ }
31
+ },
32
+ "suggest": {
33
+ "league/flysystem": "Required to use the Flysystem local and FTP drivers (~1.0).",
34
+ "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (~1.0).",
35
+ "league/flysystem-rackspace": "Required to use the Flysystem Rackspace driver (~1.0)."
36
+ },
37
+ "config": {
38
+ "sort-packages": true
39
+ },
40
+ "minimum-stability": "dev"
41
+ }
vendor/illuminate/support/AggregateServiceProvider.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support;
4
+
5
+ class AggregateServiceProvider extends ServiceProvider
6
+ {
7
+ /**
8
+ * The provider class names.
9
+ *
10
+ * @var array
11
+ */
12
+ protected $providers = [];
13
+
14
+ /**
15
+ * An array of the service provider instances.
16
+ *
17
+ * @var array
18
+ */
19
+ protected $instances = [];
20
+
21
+ /**
22
+ * Register the service provider.
23
+ *
24
+ * @return void
25
+ */
26
+ public function register()
27
+ {
28
+ $this->instances = [];
29
+
30
+ foreach ($this->providers as $provider) {
31
+ $this->instances[] = $this->app->register($provider);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Get the services provided by the provider.
37
+ *
38
+ * @return array
39
+ */
40
+ public function provides()
41
+ {
42
+ $provides = [];
43
+
44
+ foreach ($this->providers as $provider) {
45
+ $instance = $this->app->resolveProvider($provider);
46
+
47
+ $provides = array_merge($provides, $instance->provides());
48
+ }
49
+
50
+ return $provides;
51
+ }
52
+ }
vendor/illuminate/support/Arr.php ADDED
@@ -0,0 +1,602 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support;
4
+
5
+ use ArrayAccess;
6
+ use InvalidArgumentException;
7
+ use Illuminate\Support\Traits\Macroable;
8
+
9
+ class Arr
10
+ {
11
+ use Macroable;
12
+
13
+ /**
14
+ * Determine whether the given value is array accessible.
15
+ *
16
+ * @param mixed $value
17
+ * @return bool
18
+ */
19
+ public static function accessible($value)
20
+ {
21
+ return is_array($value) || $value instanceof ArrayAccess;
22
+ }
23
+
24
+ /**
25
+ * Add an element to an array using "dot" notation if it doesn't exist.
26
+ *
27
+ * @param array $array
28
+ * @param string $key
29
+ * @param mixed $value
30
+ * @return array
31
+ */
32
+ public static function add($array, $key, $value)
33
+ {
34
+ if (is_null(static::get($array, $key))) {
35
+ static::set($array, $key, $value);
36
+ }
37
+
38
+ return $array;
39
+ }
40
+
41
+ /**
42
+ * Collapse an array of arrays into a single array.
43
+ *
44
+ * @param array $array
45
+ * @return array
46
+ */
47
+ public static function collapse($array)
48
+ {
49
+ $results = [];
50
+
51
+ foreach ($array as $values) {
52
+ if ($values instanceof Collection) {
53
+ $values = $values->all();
54
+ } elseif (! is_array($values)) {
55
+ continue;
56
+ }
57
+
58
+ $results = array_merge($results, $values);
59
+ }
60
+
61
+ return $results;
62
+ }
63
+
64
+ /**
65
+ * Cross join the given arrays, returning all possible permutations.
66
+ *
67
+ * @param array ...$arrays
68
+ * @return array
69
+ */
70
+ public static function crossJoin(...$arrays)
71
+ {
72
+ $results = [[]];
73
+
74
+ foreach ($arrays as $index => $array) {
75
+ $append = [];
76
+
77
+ foreach ($results as $product) {
78
+ foreach ($array as $item) {
79
+ $product[$index] = $item;
80
+
81
+ $append[] = $product;
82
+ }
83
+ }
84
+
85
+ $results = $append;
86
+ }
87
+
88
+ return $results;
89
+ }
90
+
91
+ /**
92
+ * Divide an array into two arrays. One with keys and the other with values.
93
+ *
94
+ * @param array $array
95
+ * @return array
96
+ */
97
+ public static function divide($array)
98
+ {
99
+ return [array_keys($array), array_values($array)];
100
+ }
101
+
102
+ /**
103
+ * Flatten a multi-dimensional associative array with dots.
104
+ *
105
+ * @param array $array
106
+ * @param string $prepend
107
+ * @return array
108
+ */
109
+ public static function dot($array, $prepend = '')
110
+ {
111
+ $results = [];
112
+
113
+ foreach ($array as $key => $value) {
114
+ if (is_array($value) && ! empty($value)) {
115
+ $results = array_merge($results, static::dot($value, $prepend.$key.'.'));
116
+ } else {
117
+ $results[$prepend.$key] = $value;
118
+ }
119
+ }
120
+
121
+ return $results;
122
+ }
123
+
124
+ /**
125
+ * Get all of the given array except for a specified array of items.
126
+ *
127
+ * @param array $array
128
+ * @param array|string $keys
129
+ * @return array
130
+ */
131
+ public static function except($array, $keys)
132
+ {
133
+ static::forget($array, $keys);
134
+
135
+ return $array;
136
+ }
137
+
138
+ /**
139
+ * Determine if the given key exists in the provided array.
140
+ *
141
+ * @param \ArrayAccess|array $array
142
+ * @param string|int $key
143
+ * @return bool
144
+ */
145
+ public static function exists($array, $key)
146
+ {
147
+ if ($array instanceof ArrayAccess) {
148
+ return $array->offsetExists($key);
149
+ }
150
+
151
+ return array_key_exists($key, $array);
152
+ }
153
+
154
+ /**
155
+ * Return the first element in an array passing a given truth test.
156
+ *
157
+ * @param array $array
158
+ * @param callable|null $callback
159
+ * @param mixed $default
160
+ * @return mixed
161
+ */
162
+ public static function first($array, callable $callback = null, $default = null)
163
+ {
164
+ if (is_null($callback)) {
165
+ if (empty($array)) {
166
+ return value($default);
167
+ }
168
+
169
+ foreach ($array as $item) {
170
+ return $item;
171
+ }
172
+ }
173
+
174
+ foreach ($array as $key => $value) {
175
+ if (call_user_func($callback, $value, $key)) {
176
+ return $value;
177
+ }
178
+ }
179
+
180
+ return value($default);
181
+ }
182
+
183
+ /**
184
+ * Return the last element in an array passing a given truth test.
185
+ *
186
+ * @param array $array
187
+ * @param callable|null $callback
188
+ * @param mixed $default
189
+ * @return mixed
190
+ */
191
+ public static function last($array, callable $callback = null, $default = null)
192
+ {
193
+ if (is_null($callback)) {
194
+ return empty($array) ? value($default) : end($array);
195
+ }
196
+
197
+ return static::first(array_reverse($array, true), $callback, $default);
198
+ }
199
+
200
+ /**
201
+ * Flatten a multi-dimensional array into a single level.
202
+ *
203
+ * @param array $array
204
+ * @param int $depth
205
+ * @return array
206
+ */
207
+ public static function flatten($array, $depth = INF)
208
+ {
209
+ return array_reduce($array, function ($result, $item) use ($depth) {
210
+ $item = $item instanceof Collection ? $item->all() : $item;
211
+
212
+ if (! is_array($item)) {
213
+ return array_merge($result, [$item]);
214
+ } elseif ($depth === 1) {
215
+ return array_merge($result, array_values($item));
216
+ } else {
217
+ return array_merge($result, static::flatten($item, $depth - 1));
218
+ }
219
+ }, []);
220
+ }
221
+
222
+ /**
223
+ * Remove one or many array items from a given array using "dot" notation.
224
+ *
225
+ * @param array $array
226
+ * @param array|string $keys
227
+ * @return void
228
+ */
229
+ public static function forget(&$array, $keys)
230
+ {
231
+ $original = &$array;
232
+
233
+ $keys = (array) $keys;
234
+
235
+ if (count($keys) === 0) {
236
+ return;
237
+ }
238
+
239
+ foreach ($keys as $key) {
240
+ // if the exact key exists in the top-level, remove it
241
+ if (static::exists($array, $key)) {
242
+ unset($array[$key]);
243
+
244
+ continue;
245
+ }
246
+
247
+ $parts = explode('.', $key);
248
+
249
+ // clean up before each pass
250
+ $array = &$original;
251
+
252
+ while (count($parts) > 1) {
253
+ $part = array_shift($parts);
254
+
255
+ if (isset($array[$part]) && is_array($array[$part])) {
256
+ $array = &$array[$part];
257
+ } else {
258
+ continue 2;
259
+ }
260
+ }
261
+
262
+ unset($array[array_shift($parts)]);
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Get an item from an array using "dot" notation.
268
+ *
269
+ * @param \ArrayAccess|array $array
270
+ * @param string $key
271
+ * @param mixed $default
272
+ * @return mixed
273
+ */
274
+ public static function get($array, $key, $default = null)
275
+ {
276
+ if (! static::accessible($array)) {
277
+ return value($default);
278
+ }
279
+
280
+ if (is_null($key)) {
281
+ return $array;
282
+ }
283
+
284
+ if (static::exists($array, $key)) {
285
+ return $array[$key];
286
+ }
287
+
288
+ foreach (explode('.', $key) as $segment) {
289
+ if (static::accessible($array) && static::exists($array, $segment)) {
290
+ $array = $array[$segment];
291
+ } else {
292
+ return value($default);
293
+ }
294
+ }
295
+
296
+ return $array;
297
+ }
298
+
299
+ /**
300
+ * Check if an item or items exist in an array using "dot" notation.
301
+ *
302
+ * @param \ArrayAccess|array $array
303
+ * @param string|array $keys
304
+ * @return bool
305
+ */
306
+ public static function has($array, $keys)
307
+ {
308
+ if (is_null($keys)) {
309
+ return false;
310
+ }
311
+
312
+ $keys = (array) $keys;
313
+
314
+ if (! $array) {
315
+ return false;
316
+ }
317
+
318
+ if ($keys === []) {
319
+ return false;
320
+ }
321
+
322
+ foreach ($keys as $key) {
323
+ $subKeyArray = $array;
324
+
325
+ if (static::exists($array, $key)) {
326
+ continue;
327
+ }
328
+
329
+ foreach (explode('.', $key) as $segment) {
330
+ if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
331
+ $subKeyArray = $subKeyArray[$segment];
332
+ } else {
333
+ return false;
334
+ }
335
+ }
336
+ }
337
+
338
+ return true;
339
+ }
340
+
341
+ /**
342
+ * Determines if an array is associative.
343
+ *
344
+ * An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
345
+ *
346
+ * @param array $array
347
+ * @return bool
348
+ */
349
+ public static function isAssoc(array $array)
350
+ {
351
+ $keys = array_keys($array);
352
+
353
+ return array_keys($keys) !== $keys;
354
+ }
355
+
356
+ /**
357
+ * Get a subset of the items from the given array.
358
+ *
359
+ * @param array $array
360
+ * @param array|string $keys
361
+ * @return array
362
+ */
363
+ public static function only($array, $keys)
364
+ {
365
+ return array_intersect_key($array, array_flip((array) $keys));
366
+ }
367
+
368
+ /**
369
+ * Pluck an array of values from an array.
370
+ *
371
+ * @param array $array
372
+ * @param string|array $value
373
+ * @param string|array|null $key
374
+ * @return array
375
+ */
376
+ public static function pluck($array, $value, $key = null)
377
+ {
378
+ $results = [];
379
+
380
+ list($value, $key) = static::explodePluckParameters($value, $key);
381
+
382
+ foreach ($array as $item) {
383
+ $itemValue = data_get($item, $value);
384
+
385
+ // If the key is "null", we will just append the value to the array and keep
386
+ // looping. Otherwise we will key the array using the value of the key we
387
+ // received from the developer. Then we'll return the final array form.
388
+ if (is_null($key)) {
389
+ $results[] = $itemValue;
390
+ } else {
391
+ $itemKey = data_get($item, $key);
392
+
393
+ if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
394
+ $itemKey = (string) $itemKey;
395
+ }
396
+
397
+ $results[$itemKey] = $itemValue;
398
+ }
399
+ }
400
+
401
+ return $results;
402
+ }
403
+
404
+ /**
405
+ * Explode the "value" and "key" arguments passed to "pluck".
406
+ *
407
+ * @param string|array $value
408
+ * @param string|array|null $key
409
+ * @return array
410
+ */
411
+ protected static function explodePluckParameters($value, $key)
412
+ {
413
+ $value = is_string($value) ? explode('.', $value) : $value;
414
+
415
+ $key = is_null($key) || is_array($key) ? $key : explode('.', $key);
416
+
417
+ return [$value, $key];
418
+ }
419
+
420
+ /**
421
+ * Push an item onto the beginning of an array.
422
+ *
423
+ * @param array $array
424
+ * @param mixed $value
425
+ * @param mixed $key
426
+ * @return array
427
+ */
428
+ public static function prepend($array, $value, $key = null)
429
+ {
430
+ if (is_null($key)) {
431
+ array_unshift($array, $value);
432
+ } else {
433
+ $array = [$key => $value] + $array;
434
+ }
435
+
436
+ return $array;
437
+ }
438
+
439
+ /**
440
+ * Get a value from the array, and remove it.
441
+ *
442
+ * @param array $array
443
+ * @param string $key
444
+ * @param mixed $default
445
+ * @return mixed
446
+ */
447
+ public static function pull(&$array, $key, $default = null)
448
+ {
449
+ $value = static::get($array, $key, $default);
450
+
451
+ static::forget($array, $key);
452
+
453
+ return $value;
454
+ }
455
+
456
+ /**
457
+ * Get one or a specified number of random values from an array.
458
+ *
459
+ * @param array $array
460
+ * @param int|null $number
461
+ * @return mixed
462
+ *
463
+ * @throws \InvalidArgumentException
464
+ */
465
+ public static function random($array, $number = null)
466
+ {
467
+ $requested = is_null($number) ? 1 : $number;
468
+
469
+ $count = count($array);
470
+
471
+ if ($requested > $count) {
472
+ throw new InvalidArgumentException(
473
+ "You requested {$requested} items, but there are only {$count} items available."
474
+ );
475
+ }
476
+
477
+ if (is_null($number)) {
478
+ return $array[array_rand($array)];
479
+ }
480
+
481
+ if ((int) $number === 0) {
482
+ return [];
483
+ }
484
+
485
+ $keys = array_rand($array, $number);
486
+
487
+ $results = [];
488
+
489
+ foreach ((array) $keys as $key) {
490
+ $results[] = $array[$key];
491
+ }
492
+
493
+ return $results;
494
+ }
495
+
496
+ /**
497
+ * Set an array item to a given value using "dot" notation.
498
+ *
499
+ * If no key is given to the method, the entire array will be replaced.
500
+ *
501
+ * @param array $array
502
+ * @param string $key
503
+ * @param mixed $value
504
+ * @return array
505
+ */
506
+ public static function set(&$array, $key, $value)
507
+ {
508
+ if (is_null($key)) {
509
+ return $array = $value;
510
+ }
511
+
512
+ $keys = explode('.', $key);
513
+
514
+ while (count($keys) > 1) {
515
+ $key = array_shift($keys);
516
+
517
+ // If the key doesn't exist at this depth, we will just create an empty array
518
+ // to hold the next value, allowing us to create the arrays to hold final
519
+ // values at the correct depth. Then we'll keep digging into the array.
520
+ if (! isset($array[$key]) || ! is_array($array[$key])) {
521
+ $array[$key] = [];
522
+ }
523
+
524
+ $array = &$array[$key];
525
+ }
526
+
527
+ $array[array_shift($keys)] = $value;
528
+
529
+ return $array;
530
+ }
531
+
532
+ /**
533
+ * Shuffle the given array and return the result.
534
+ *
535
+ * @param array $array
536
+ * @return array
537
+ */
538
+ public static function shuffle($array)
539
+ {
540
+ shuffle($array);
541
+
542
+ return $array;
543
+ }
544
+
545
+ /**
546
+ * Sort the array using the given callback or "dot" notation.
547
+ *
548
+ * @param array $array
549
+ * @param callable|string $callback
550
+ * @return array
551
+ */
552
+ public static function sort($array, $callback)
553
+ {
554
+ return Collection::make($array)->sortBy($callback)->all();
555
+ }
556
+
557
+ /**
558
+ * Recursively sort an array by keys and values.
559
+ *
560
+ * @param array $array
561
+ * @return array
562
+ */
563
+ public static function sortRecursive($array)
564
+ {
565
+ foreach ($array as &$value) {
566
+ if (is_array($value)) {
567
+ $value = static::sortRecursive($value);
568
+ }
569
+ }
570
+
571
+ if (static::isAssoc($array)) {
572
+ ksort($array);
573
+ } else {
574
+ sort($array);
575
+ }
576
+
577
+ return $array;
578
+ }
579
+
580
+ /**
581
+ * Filter the array using the given callback.
582
+ *
583
+ * @param array $array
584
+ * @param callable $callback
585
+ * @return array
586
+ */
587
+ public static function where($array, callable $callback)
588
+ {
589
+ return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
590
+ }
591
+
592
+ /**
593
+ * If the given value is not an array, wrap it in one.
594
+ *
595
+ * @param mixed $value
596
+ * @return array
597
+ */
598
+ public static function wrap($value)
599
+ {
600
+ return ! is_array($value) ? [$value] : $value;
601
+ }
602
+ }
vendor/illuminate/support/Collection.php ADDED
@@ -0,0 +1,1655 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support;
4
+
5
+ use Countable;
6
+ use Exception;
7
+ use ArrayAccess;
8
+ use Traversable;
9
+ use ArrayIterator;
10
+ use CachingIterator;
11
+ use JsonSerializable;
12
+ use IteratorAggregate;
13
+ use Illuminate\Support\Traits\Macroable;
14
+ use Illuminate\Contracts\Support\Jsonable;
15
+ use Illuminate\Contracts\Support\Arrayable;
16
+
17
+ class Collection implements ArrayAccess, Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable
18
+ {
19
+ use Macroable;
20
+
21
+ /**
22
+ * The items contained in the collection.
23
+ *
24
+ * @var array
25
+ */
26
+ protected $items = [];
27
+
28
+ /**
29
+ * The methods that can be proxied.
30
+ *
31
+ * @var array
32
+ */
33
+ protected static $proxies = [
34
+ 'average', 'avg', 'contains', 'each', 'every', 'filter', 'first', 'flatMap',
35
+ 'map', 'partition', 'reject', 'sortBy', 'sortByDesc', 'sum',
36
+ ];
37
+
38
+ /**
39
+ * Create a new collection.
40
+ *
41
+ * @param mixed $items
42
+ * @return void
43
+ */
44
+ public function __construct($items = [])
45
+ {
46
+ $this->items = $this->getArrayableItems($items);
47
+ }
48
+
49
+ /**
50
+ * Create a new collection instance if the value isn't one already.
51
+ *
52
+ * @param mixed $items
53
+ * @return static
54
+ */
55
+ public static function make($items = [])
56
+ {
57
+ return new static($items);
58
+ }
59
+
60
+ /**
61
+ * Create a new collection by invoking the callback a given number of times.
62
+ *
63
+ * @param int $number
64
+ * @param callable $callback
65
+ * @return static
66
+ */
67
+ public static function times($number, callable $callback = null)
68
+ {
69
+ if ($number < 1) {
70
+ return new static;
71
+ }
72
+
73
+ if (is_null($callback)) {
74
+ return new static(range(1, $number));
75
+ }
76
+
77
+ return (new static(range(1, $number)))->map($callback);
78
+ }
79
+
80
+ /**
81
+ * Get all of the items in the collection.
82
+ *
83
+ * @return array
84
+ */
85
+ public function all()
86
+ {
87
+ return $this->items;
88
+ }
89
+
90
+ /**
91
+ * Get the average value of a given key.
92
+ *
93
+ * @param callable|string|null $callback
94
+ * @return mixed
95
+ */
96
+ public function avg($callback = null)
97
+ {
98
+ if ($count = $this->count()) {
99
+ return $this->sum($callback) / $count;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Alias for the "avg" method.
105
+ *
106
+ * @param callable|string|null $callback
107
+ * @return mixed
108
+ */
109
+ public function average($callback = null)
110
+ {
111
+ return $this->avg($callback);
112
+ }
113
+
114
+ /**
115
+ * Get the median of a given key.
116
+ *
117
+ * @param null $key
118
+ * @return mixed
119
+ */
120
+ public function median($key = null)
121
+ {
122
+ $count = $this->count();
123
+
124
+ if ($count == 0) {
125
+ return;
126
+ }
127
+
128
+ $values = with(isset($key) ? $this->pluck($key) : $this)
129
+ ->sort()->values();
130
+
131
+ $middle = (int) ($count / 2);
132
+
133
+ if ($count % 2) {
134
+ return $values->get($middle);
135
+ }
136
+
137
+ return (new static([
138
+ $values->get($middle - 1), $values->get($middle),
139
+ ]))->average();
140
+ }
141
+
142
+ /**
143
+ * Get the mode of a given key.
144
+ *
145
+ * @param mixed $key
146
+ * @return array|null
147
+ */
148
+ public function mode($key = null)
149
+ {
150
+ $count = $this->count();
151
+
152
+ if ($count == 0) {
153
+ return;
154
+ }
155
+
156
+ $collection = isset($key) ? $this->pluck($key) : $this;
157
+
158
+ $counts = new self;
159
+
160
+ $collection->each(function ($value) use ($counts) {
161
+ $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1;
162
+ });
163
+
164
+ $sorted = $counts->sort();
165
+
166
+ $highestValue = $sorted->last();
167
+
168
+ return $sorted->filter(function ($value) use ($highestValue) {
169
+ return $value == $highestValue;
170
+ })->sort()->keys()->all();
171
+ }
172
+
173
+ /**
174
+ * Collapse the collection of items into a single array.
175
+ *
176
+ * @return static
177
+ */
178
+ public function collapse()
179
+ {
180
+ return new static(Arr::collapse($this->items));
181
+ }
182
+
183
+ /**
184
+ * Determine if an item exists in the collection.
185
+ *
186
+ * @param mixed $key
187
+ * @param mixed $operator
188
+ * @param mixed $value
189
+ * @return bool
190
+ */
191
+ public function contains($key, $operator = null, $value = null)
192
+ {
193
+ if (func_num_args() == 1) {
194
+ if ($this->useAsCallable($key)) {
195
+ return ! is_null($this->first($key));
196
+ }
197
+
198
+ return in_array($key, $this->items);
199
+ }
200
+
201
+ if (func_num_args() == 2) {
202
+ $value = $operator;
203
+
204
+ $operator = '=';
205
+ }
206
+
207
+ return $this->contains($this->operatorForWhere($key, $operator, $value));
208
+ }
209
+
210
+ /**
211
+ * Determine if an item exists in the collection using strict comparison.
212
+ *
213
+ * @param mixed $key
214
+ * @param mixed $value
215
+ * @return bool
216
+ */
217
+ public function containsStrict($key, $value = null)
218
+ {
219
+ if (func_num_args() == 2) {
220
+ return $this->contains(function ($item) use ($key, $value) {
221
+ return data_get($item, $key) === $value;
222
+ });
223
+ }
224
+
225
+ if ($this->useAsCallable($key)) {
226
+ return ! is_null($this->first($key));
227
+ }
228
+
229
+ return in_array($key, $this->items, true);
230
+ }
231
+
232
+ /**
233
+ * Cross join with the given lists, returning all possible permutations.
234
+ *
235
+ * @param mixed ...$lists
236
+ * @return static
237
+ */
238
+ public function crossJoin(...$lists)
239
+ {
240
+ return new static(Arr::crossJoin(
241
+ $this->items, ...array_map([$this, 'getArrayableItems'], $lists)
242
+ ));
243
+ }
244
+
245
+ /**
246
+ * Get the items in the collection that are not present in the given items.
247
+ *
248
+ * @param mixed $items
249
+ * @return static
250
+ */
251
+ public function diff($items)
252
+ {
253
+ return new static(array_diff($this->items, $this->getArrayableItems($items)));
254
+ }
255
+
256
+ /**
257
+ * Get the items in the collection whose keys and values are not present in the given items.
258
+ *
259
+ * @param mixed $items
260
+ * @return static
261
+ */
262
+ public function diffAssoc($items)
263
+ {
264
+ return new static(array_diff_assoc($this->items, $this->getArrayableItems($items)));
265
+ }
266
+
267
+ /**
268
+ * Get the items in the collection whose keys are not present in the given items.
269
+ *
270
+ * @param mixed $items
271
+ * @return static
272
+ */
273
+ public function diffKeys($items)
274
+ {
275
+ return new static(array_diff_key($this->items, $this->getArrayableItems($items)));
276
+ }
277
+
278
+ /**
279
+ * Execute a callback over each item.
280
+ *
281
+ * @param callable $callback
282
+ * @return $this
283
+ */
284
+ public function each(callable $callback)
285
+ {
286
+ foreach ($this->items as $key => $item) {
287
+ if ($callback($item, $key) === false) {
288
+ break;
289
+ }
290
+ }
291
+
292
+ return $this;
293
+ }
294
+
295
+ /**
296
+ * Execute a callback over each nested chunk of items.
297
+ *
298
+ * @param callable $callback
299
+ * @return static
300
+ */
301
+ public function eachSpread(callable $callback)
302
+ {
303
+ return $this->each(function ($chunk) use ($callback) {
304
+ return $callback(...$chunk);
305
+ });
306
+ }
307
+
308
+ /**
309
+ * Determine if all items in the collection pass the given test.
310
+ *
311
+ * @param string|callable $key
312
+ * @param mixed $operator
313
+ * @param mixed $value
314
+ * @return bool
315
+ */
316
+ public function every($key, $operator = null, $value = null)
317
+ {
318
+ if (func_num_args() == 1) {
319
+ $callback = $this->valueRetriever($key);
320
+
321
+ foreach ($this->items as $k => $v) {
322
+ if (! $callback($v, $k)) {
323
+ return false;
324
+ }
325
+ }
326
+
327
+ return true;
328
+ }
329
+
330
+ if (func_num_args() == 2) {
331
+ $value = $operator;
332
+
333
+ $operator = '=';
334
+ }
335
+
336
+ return $this->every($this->operatorForWhere($key, $operator, $value));
337
+ }
338
+
339
+ /**
340
+ * Get all items except for those with the specified keys.
341
+ *
342
+ * @param mixed $keys
343
+ * @return static
344
+ */
345
+ public function except($keys)
346
+ {
347
+ $keys = is_array($keys) ? $keys : func_get_args();
348
+
349
+ return new static(Arr::except($this->items, $keys));
350
+ }
351
+
352
+ /**
353
+ * Run a filter over each of the items.
354
+ *
355
+ * @param callable|null $callback
356
+ * @return static
357
+ */
358
+ public function filter(callable $callback = null)
359
+ {
360
+ if ($callback) {
361
+ return new static(Arr::where($this->items, $callback));
362
+ }
363
+
364
+ return new static(array_filter($this->items));
365
+ }
366
+
367
+ /**
368
+ * Apply the callback if the value is truthy.
369
+ *
370
+ * @param bool $value
371
+ * @param callable $callback
372
+ * @param callable $default
373
+ * @return mixed
374
+ */
375
+ public function when($value, callable $callback, callable $default = null)
376
+ {
377
+ if ($value) {
378
+ return $callback($this);
379
+ } elseif ($default) {
380
+ return $default($this);
381
+ }
382
+
383
+ return $this;
384
+ }
385
+
386
+ /**
387
+ * Apply the callback if the value is falsy.
388
+ *
389
+ * @param bool $value
390
+ * @param callable $callback
391
+ * @param callable $default
392
+ * @return mixed
393
+ */
394
+ public function unless($value, callable $callback, callable $default = null)
395
+ {
396
+ return $this->when(! $value, $callback, $default);
397
+ }
398
+
399
+ /**
400
+ * Filter items by the given key value pair.
401
+ *
402
+ * @param string $key
403
+ * @param mixed $operator
404
+ * @param mixed $value
405
+ * @return static
406
+ */
407
+ public function where($key, $operator, $value = null)
408
+ {
409
+ if (func_num_args() == 2) {
410
+ $value = $operator;
411
+
412
+ $operator = '=';
413
+ }
414
+
415
+ return $this->filter($this->operatorForWhere($key, $operator, $value));
416
+ }
417
+
418
+ /**
419
+ * Get an operator checker callback.
420
+ *
421
+ * @param string $key
422
+ * @param string $operator
423
+ * @param mixed $value
424
+ * @return \Closure
425
+ */
426
+ protected function operatorForWhere($key, $operator, $value)
427
+ {
428
+ return function ($item) use ($key, $operator, $value) {
429
+ $retrieved = data_get($item, $key);
430
+
431
+ switch ($operator) {
432
+ default:
433
+ case '=':
434
+ case '==': return $retrieved == $value;
435
+ case '!=':
436
+ case '<>': return $retrieved != $value;
437
+ case '<': return $retrieved < $value;
438
+ case '>': return $retrieved > $value;
439
+ case '<=': return $retrieved <= $value;
440
+ case '>=': return $retrieved >= $value;
441
+ case '===': return $retrieved === $value;
442
+ case '!==': return $retrieved !== $value;
443
+ }
444
+ };
445
+ }
446
+
447
+ /**
448
+ * Filter items by the given key value pair using strict comparison.
449
+ *
450
+ * @param string $key
451
+ * @param mixed $value
452
+ * @return static
453
+ */
454
+ public function whereStrict($key, $value)
455
+ {
456
+ return $this->where($key, '===', $value);
457
+ }
458
+
459
+ /**
460
+ * Filter items by the given key value pair.
461
+ *
462
+ * @param string $key
463
+ * @param mixed $values
464
+ * @param bool $strict
465
+ * @return static
466
+ */
467
+ public function whereIn($key, $values, $strict = false)
468
+ {
469
+ $values = $this->getArrayableItems($values);
470
+
471
+ return $this->filter(function ($item) use ($key, $values, $strict) {
472
+ return in_array(data_get($item, $key), $values, $strict);
473
+ });
474
+ }
475
+
476
+ /**
477
+ * Filter items by the given key value pair using strict comparison.
478
+ *
479
+ * @param string $key
480
+ * @param mixed $values
481
+ * @return static
482
+ */
483
+ public function whereInStrict($key, $values)
484
+ {
485
+ return $this->whereIn($key, $values, true);
486
+ }
487
+
488
+ /**
489
+ * Filter items by the given key value pair.
490
+ *
491
+ * @param string $key
492
+ * @param mixed $values
493
+ * @param bool $strict
494
+ * @return static
495
+ */
496
+ public function whereNotIn($key, $values, $strict = false)
497
+ {
498
+ $values = $this->getArrayableItems($values);
499
+
500
+ return $this->reject(function ($item) use ($key, $values, $strict) {
501
+ return in_array(data_get($item, $key), $values, $strict);
502
+ });
503
+ }
504
+
505
+ /**
506
+ * Filter items by the given key value pair using strict comparison.
507
+ *
508
+ * @param string $key
509
+ * @param mixed $values
510
+ * @return static
511
+ */
512
+ public function whereNotInStrict($key, $values)
513
+ {
514
+ return $this->whereNotIn($key, $values, true);
515
+ }
516
+
517
+ /**
518
+ * Get the first item from the collection.
519
+ *
520
+ * @param callable|null $callback
521
+ * @param mixed $default
522
+ * @return mixed
523
+ */
524
+ public function first(callable $callback = null, $default = null)
525
+ {
526
+ return Arr::first($this->items, $callback, $default);
527
+ }
528
+
529
+ /**
530
+ * Get a flattened array of the items in the collection.
531
+ *
532
+ * @param int $depth
533
+ * @return static
534
+ */
535
+ public function flatten($depth = INF)
536
+ {
537
+ return new static(Arr::flatten($this->items, $depth));
538
+ }
539
+
540
+ /**
541
+ * Flip the items in the collection.
542
+ *
543
+ * @return static
544
+ */
545
+ public function flip()
546
+ {
547
+ return new static(array_flip($this->items));
548
+ }
549
+
550
+ /**
551
+ * Remove an item from the collection by key.
552
+ *
553
+ * @param string|array $keys
554
+ * @return $this
555
+ */
556
+ public function forget($keys)
557
+ {
558
+ foreach ((array) $keys as $key) {
559
+ $this->offsetUnset($key);
560
+ }
561
+
562
+ return $this;
563
+ }
564
+
565
+ /**
566
+ * Get an item from the collection by key.
567
+ *
568
+ * @param mixed $key
569
+ * @param mixed $default
570
+ * @return mixed
571
+ */
572
+ public function get($key, $default = null)
573
+ {
574
+ if ($this->offsetExists($key)) {
575
+ return $this->items[$key];
576
+ }
577
+
578
+ return value($default);
579
+ }
580
+
581
+ /**
582
+ * Group an associative array by a field or using a callback.
583
+ *
584
+ * @param callable|string $groupBy
585
+ * @param bool $preserveKeys
586
+ * @return static
587
+ */
588
+ public function groupBy($groupBy, $preserveKeys = false)
589
+ {
590
+ $groupBy = $this->valueRetriever($groupBy);
591
+
592
+ $results = [];
593
+
594
+ foreach ($this->items as $key => $value) {
595
+ $groupKeys = $groupBy($value, $key);
596
+
597
+ if (! is_array($groupKeys)) {
598
+ $groupKeys = [$groupKeys];
599
+ }
600
+
601
+ foreach ($groupKeys as $groupKey) {
602
+ $groupKey = is_bool($groupKey) ? (int) $groupKey : $groupKey;
603
+
604
+ if (! array_key_exists($groupKey, $results)) {
605
+ $results[$groupKey] = new static;
606
+ }
607
+
608
+ $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value);
609
+ }
610
+ }
611
+
612
+ return new static($results);
613
+ }
614
+
615
+ /**
616
+ * Key an associative array by a field or using a callback.
617
+ *
618
+ * @param callable|string $keyBy
619
+ * @return static
620
+ */
621
+ public function keyBy($keyBy)
622
+ {
623
+ $keyBy = $this->valueRetriever($keyBy);
624
+
625
+ $results = [];
626
+
627
+ foreach ($this->items as $key => $item) {
628
+ $resolvedKey = $keyBy($item, $key);
629
+
630
+ if (is_object($resolvedKey)) {
631
+ $resolvedKey = (string) $resolvedKey;
632
+ }
633
+
634
+ $results[$resolvedKey] = $item;
635
+ }
636
+
637
+ return new static($results);
638
+ }
639
+
640
+ /**
641
+ * Determine if an item exists in the collection by key.
642
+ *
643
+ * @param mixed $key
644
+ * @return bool
645
+ */
646
+ public function has($key)
647
+ {
648
+ return $this->offsetExists($key);
649
+ }
650
+
651
+ /**
652
+ * Concatenate values of a given key as a string.
653
+ *
654
+ * @param string $value
655
+ * @param string $glue
656
+ * @return string
657
+ */
658
+ public function implode($value, $glue = null)
659
+ {
660
+ $first = $this->first();
661
+
662
+ if (is_array($first) || is_object($first)) {
663
+ return implode($glue, $this->pluck($value)->all());
664
+ }
665
+
666
+ return implode($value, $this->items);
667
+ }
668
+
669
+ /**
670
+ * Intersect the collection with the given items.
671
+ *
672
+ * @param mixed $items
673
+ * @return static
674
+ */
675
+ public function intersect($items)
676
+ {
677
+ return new static(array_intersect($this->items, $this->getArrayableItems($items)));
678
+ }
679
+
680
+ /**
681
+ * Intersect the collection with the given items by key.
682
+ *
683
+ * @param mixed $items
684
+ * @return static
685
+ */
686
+ public function intersectKey($items)
687
+ {
688
+ return new static(array_intersect_key($this->items, $this->getArrayableItems($items)));
689
+ }
690
+
691
+ /**
692
+ * Determine if the collection is empty or not.
693
+ *
694
+ * @return bool
695
+ */
696
+ public function isEmpty()
697
+ {
698
+ return empty($this->items);
699
+ }
700
+
701
+ /**
702
+ * Determine if the collection is not empty.
703
+ *
704
+ * @return bool
705
+ */
706
+ public function isNotEmpty()
707
+ {
708
+ return ! $this->isEmpty();
709
+ }
710
+
711
+ /**
712
+ * Determine if the given value is callable, but not a string.
713
+ *
714
+ * @param mixed $value
715
+ * @return bool
716
+ */
717
+ protected function useAsCallable($value)
718
+ {
719
+ return ! is_string($value) && is_callable($value);
720
+ }
721
+
722
+ /**
723
+ * Get the keys of the collection items.
724
+ *
725
+ * @return static
726
+ */
727
+ public function keys()
728
+ {
729
+ return new static(array_keys($this->items));
730
+ }
731
+
732
+ /**
733
+ * Get the last item from the collection.
734
+ *
735
+ * @param callable|null $callback
736
+ * @param mixed $default
737
+ * @return mixed
738
+ */
739
+ public function last(callable $callback = null, $default = null)
740
+ {
741
+ return Arr::last($this->items, $callback, $default);
742
+ }
743
+
744
+ /**
745
+ * Get the values of a given key.
746
+ *
747
+ * @param string|array $value
748
+ * @param string|null $key
749
+ * @return static
750
+ */
751
+ public function pluck($value, $key = null)
752
+ {
753
+ return new static(Arr::pluck($this->items, $value, $key));
754
+ }
755
+
756
+ /**
757
+ * Run a map over each of the items.
758
+ *
759
+ * @param callable $callback
760
+ * @return static
761
+ */
762
+ public function map(callable $callback)
763
+ {
764
+ $keys = array_keys($this->items);
765
+
766
+ $items = array_map($callback, $this->items, $keys);
767
+
768
+ return new static(array_combine($keys, $items));
769
+ }
770
+
771
+ /**
772
+ * Run a map over each nested chunk of items.
773
+ *
774
+ * @param callable $callback
775
+ * @return static
776
+ */
777
+ public function mapSpread(callable $callback)
778
+ {
779
+ return $this->map(function ($chunk) use ($callback) {
780
+ return $callback(...$chunk);
781
+ });
782
+ }
783
+
784
+ /**
785
+ * Run a grouping map over the items.
786
+ *
787
+ * The callback should return an associative array with a single key/value pair.
788
+ *
789
+ * @param callable $callback
790
+ * @return static
791
+ */
792
+ public function mapToGroups(callable $callback)
793
+ {
794
+ $groups = $this->map($callback)->reduce(function ($groups, $pair) {
795
+ $groups[key($pair)][] = reset($pair);
796
+
797
+ return $groups;
798
+ }, []);
799
+
800
+ return (new static($groups))->map([$this, 'make']);
801
+ }
802
+
803
+ /**
804
+ * Run an associative map over each of the items.
805
+ *
806
+ * The callback should return an associative array with a single key/value pair.
807
+ *
808
+ * @param callable $callback
809
+ * @return static
810
+ */
811
+ public function mapWithKeys(callable $callback)
812
+ {
813
+ $result = [];
814
+
815
+ foreach ($this->items as $key => $value) {
816
+ $assoc = $callback($value, $key);
817
+
818
+ foreach ($assoc as $mapKey => $mapValue) {
819
+ $result[$mapKey] = $mapValue;
820
+ }
821
+ }
822
+
823
+ return new static($result);
824
+ }
825
+
826
+ /**
827
+ * Map a collection and flatten the result by a single level.
828
+ *
829
+ * @param callable $callback
830
+ * @return static
831
+ */
832
+ public function flatMap(callable $callback)
833
+ {
834
+ return $this->map($callback)->collapse();
835
+ }
836
+
837
+ /**
838
+ * Get the max value of a given key.
839
+ *
840
+ * @param callable|string|null $callback
841
+ * @return mixed
842
+ */
843
+ public function max($callback = null)
844
+ {
845
+ $callback = $this->valueRetriever($callback);
846
+
847
+ return $this->filter(function ($value) {
848
+ return ! is_null($value);
849
+ })->reduce(function ($result, $item) use ($callback) {
850
+ $value = $callback($item);
851
+
852
+ return is_null($result) || $value > $result ? $value : $result;
853
+ });
854
+ }
855
+
856
+ /**
857
+ * Merge the collection with the given items.
858
+ *
859
+ * @param mixed $items
860
+ * @return static
861
+ */
862
+ public function merge($items)
863
+ {
864
+ return new static(array_merge($this->items, $this->getArrayableItems($items)));
865
+ }
866
+
867
+ /**
868
+ * Create a collection by using this collection for keys and another for its values.
869
+ *
870
+ * @param mixed $values
871
+ * @return static
872
+ */
873
+ public function combine($values)
874
+ {
875
+ return new static(array_combine($this->all(), $this->getArrayableItems($values)));
876
+ }
877
+
878
+ /**
879
+ * Union the collection with the given items.
880
+ *
881
+ * @param mixed $items
882
+ * @return static
883
+ */
884
+ public function union($items)
885
+ {
886
+ return new static($this->items + $this->getArrayableItems($items));
887
+ }
888
+
889
+ /**
890
+ * Get the min value of a given key.
891
+ *
892
+ * @param callable|string|null $callback
893
+ * @return mixed
894
+ */
895
+ public function min($callback = null)
896
+ {
897
+ $callback = $this->valueRetriever($callback);
898
+
899
+ return $this->filter(function ($value) {
900
+ return ! is_null($value);
901
+ })->reduce(function ($result, $item) use ($callback) {
902
+ $value = $callback($item);
903
+
904
+ return is_null($result) || $value < $result ? $value : $result;
905
+ });
906
+ }
907
+
908
+ /**
909
+ * Create a new collection consisting of every n-th element.
910
+ *
911
+ * @param int $step
912
+ * @param int $offset
913
+ * @return static
914
+ */
915
+ public function nth($step, $offset = 0)
916
+ {
917
+ $new = [];
918
+
919
+ $position = 0;
920
+
921
+ foreach ($this->items as $item) {
922
+ if ($position % $step === $offset) {
923
+ $new[] = $item;
924
+ }
925
+
926
+ $position++;
927
+ }
928
+
929
+ return new static($new);
930
+ }
931
+
932
+ /**
933
+ * Get the items with the specified keys.
934
+ *
935
+ * @param mixed $keys
936
+ * @return static
937
+ */
938
+ public function only($keys)
939
+ {
940
+ if (is_null($keys)) {
941
+ return new static($this->items);
942
+ }
943
+
944
+ $keys = is_array($keys) ? $keys : func_get_args();
945
+
946
+ return new static(Arr::only($this->items, $keys));
947
+ }
948
+
949
+ /**
950
+ * "Paginate" the collection by slicing it into a smaller collection.
951
+ *
952
+ * @param int $page
953
+ * @param int $perPage
954
+ * @return static
955
+ */
956
+ public function forPage($page, $perPage)
957
+ {
958
+ return $this->slice(($page - 1) * $perPage, $perPage);
959
+ }
960
+
961
+ /**
962
+ * Partition the collection into two arrays using the given callback or key.
963
+ *
964
+ * @param callable|string $callback
965
+ * @return static
966
+ */
967
+ public function partition($callback)
968
+ {
969
+ $partitions = [new static, new static];
970
+
971
+ $callback = $this->valueRetriever($callback);
972
+
973
+ foreach ($this->items as $key => $item) {
974
+ $partitions[(int) ! $callback($item)][$key] = $item;
975
+ }
976
+
977
+ return new static($partitions);
978
+ }
979
+
980
+ /**
981
+ * Pass the collection to the given callback and return the result.
982
+ *
983
+ * @param callable $callback
984
+ * @return mixed
985
+ */
986
+ public function pipe(callable $callback)
987
+ {
988
+ return $callback($this);
989
+ }
990
+
991
+ /**
992
+ * Get and remove the last item from the collection.
993
+ *
994
+ * @return mixed
995
+ */
996
+ public function pop()
997
+ {
998
+ return array_pop($this->items);
999
+ }
1000
+
1001
+ /**
1002
+ * Push an item onto the beginning of the collection.
1003
+ *
1004
+ * @param mixed $value
1005
+ * @param mixed $key
1006
+ * @return $this
1007
+ */
1008
+ public function prepend($value, $key = null)
1009
+ {
1010
+ $this->items = Arr::prepend($this->items, $value, $key);
1011
+
1012
+ return $this;
1013
+ }
1014
+
1015
+ /**
1016
+ * Push an item onto the end of the collection.
1017
+ *
1018
+ * @param mixed $value
1019
+ * @return $this
1020
+ */
1021
+ public function push($value)
1022
+ {
1023
+ $this->offsetSet(null, $value);
1024
+
1025
+ return $this;
1026
+ }
1027
+
1028
+ /**
1029
+ * Push all of the given items onto the collection.
1030
+ *
1031
+ * @param \Traversable $source
1032
+ * @return self
1033
+ */
1034
+ public function concat($source)
1035
+ {
1036
+ $result = new static($this);
1037
+
1038
+ foreach ($source as $item) {
1039
+ $result->push($item);
1040
+ }
1041
+
1042
+ return $result;
1043
+ }
1044
+
1045
+ /**
1046
+ * Get and remove an item from the collection.
1047
+ *
1048
+ * @param mixed $key
1049
+ * @param mixed $default
1050
+ * @return mixed
1051
+ */
1052
+ public function pull($key, $default = null)
1053
+ {
1054
+ return Arr::pull($this->items, $key, $default);
1055
+ }
1056
+
1057
+ /**
1058
+ * Put an item in the collection by key.
1059
+ *
1060
+ * @param mixed $key
1061
+ * @param mixed $value
1062
+ * @return $this
1063
+ */
1064
+ public function put($key, $value)
1065
+ {
1066
+ $this->offsetSet($key, $value);
1067
+
1068
+ return $this;
1069
+ }
1070
+
1071
+ /**
1072
+ * Get one or a specified number of items randomly from the collection.
1073
+ *
1074
+ * @param int|null $number
1075
+ * @return mixed
1076
+ *
1077
+ * @throws \InvalidArgumentException
1078
+ */
1079
+ public function random($number = null)
1080
+ {
1081
+ if (is_null($number)) {
1082
+ return Arr::random($this->items);
1083
+ }
1084
+
1085
+ return new static(Arr::random($this->items, $number));
1086
+ }
1087
+
1088
+ /**
1089
+ * Reduce the collection to a single value.
1090
+ *
1091
+ * @param callable $callback
1092
+ * @param mixed $initial
1093
+ * @return mixed
1094
+ */
1095
+ public function reduce(callable $callback, $initial = null)
1096
+ {
1097
+ return array_reduce($this->items, $callback, $initial);
1098
+ }
1099
+
1100
+ /**
1101
+ * Create a collection of all elements that do not pass a given truth test.
1102
+ *
1103
+ * @param callable|mixed $callback
1104
+ * @return static
1105
+ */
1106
+ public function reject($callback)
1107
+ {
1108
+ if ($this->useAsCallable($callback)) {
1109
+ return $this->filter(function ($value, $key) use ($callback) {
1110
+ return ! $callback($value, $key);
1111
+ });
1112
+ }
1113
+
1114
+ return $this->filter(function ($item) use ($callback) {
1115
+ return $item != $callback;
1116
+ });
1117
+ }
1118
+
1119
+ /**
1120
+ * Reverse items order.
1121
+ *
1122
+ * @return static
1123
+ */
1124
+ public function reverse()
1125
+ {
1126
+ return new static(array_reverse($this->items, true));
1127
+ }
1128
+
1129
+ /**
1130
+ * Search the collection for a given value and return the corresponding key if successful.
1131
+ *
1132
+ * @param mixed $value
1133
+ * @param bool $strict
1134
+ * @return mixed
1135
+ */
1136
+ public function search($value, $strict = false)
1137
+ {
1138
+ if (! $this->useAsCallable($value)) {
1139
+ return array_search($value, $this->items, $strict);
1140
+ }
1141
+
1142
+ foreach ($this->items as $key => $item) {
1143
+ if (call_user_func($value, $item, $key)) {
1144
+ return $key;
1145
+ }
1146
+ }
1147
+
1148
+ return false;
1149
+ }
1150
+
1151
+ /**
1152
+ * Get and remove the first item from the collection.
1153
+ *
1154
+ * @return mixed
1155
+ */
1156
+ public function shift()
1157
+ {
1158
+ return array_shift($this->items);
1159
+ }
1160
+
1161
+ /**
1162
+ * Shuffle the items in the collection.
1163
+ *
1164
+ * @param int $seed
1165
+ * @return static
1166
+ */
1167
+ public function shuffle($seed = null)
1168
+ {
1169
+ $items = $this->items;
1170
+
1171
+ if (is_null($seed)) {
1172
+ shuffle($items);
1173
+ } else {
1174
+ srand($seed);
1175
+
1176
+ usort($items, function () {
1177
+ return rand(-1, 1);
1178
+ });
1179
+ }
1180
+
1181
+ return new static($items);
1182
+ }
1183
+
1184
+ /**
1185
+ * Slice the underlying collection array.
1186
+ *
1187
+ * @param int $offset
1188
+ * @param int $length
1189
+ * @return static
1190
+ */
1191
+ public function slice($offset, $length = null)
1192
+ {
1193
+ return new static(array_slice($this->items, $offset, $length, true));
1194
+ }
1195
+
1196
+ /**
1197
+ * Split a collection into a certain number of groups.
1198
+ *
1199
+ * @param int $numberOfGroups
1200
+ * @return static
1201
+ */
1202
+ public function split($numberOfGroups)
1203
+ {
1204
+ if ($this->isEmpty()) {
1205
+ return new static;
1206
+ }
1207
+
1208
+ $groupSize = ceil($this->count() / $numberOfGroups);
1209
+
1210
+ return $this->chunk($groupSize);
1211
+ }
1212
+
1213
+ /**
1214
+ * Chunk the underlying collection array.
1215
+ *
1216
+ * @param int $size
1217
+ * @return static
1218
+ */
1219
+ public function chunk($size)
1220
+ {
1221
+ if ($size <= 0) {
1222
+ return new static;
1223
+ }
1224
+
1225
+ $chunks = [];
1226
+
1227
+ foreach (array_chunk($this->items, $size, true) as $chunk) {
1228
+ $chunks[] = new static($chunk);
1229
+ }
1230
+
1231
+ return new static($chunks);
1232
+ }
1233
+
1234
+ /**
1235
+ * Sort through each item with a callback.
1236
+ *
1237
+ * @param callable|null $callback
1238
+ * @return static
1239
+ */
1240
+ public function sort(callable $callback = null)
1241
+ {
1242
+ $items = $this->items;
1243
+
1244
+ $callback
1245
+ ? uasort($items, $callback)
1246
+ : asort($items);
1247
+
1248
+ return new static($items);
1249
+ }
1250
+
1251
+ /**
1252
+ * Sort the collection using the given callback.
1253
+ *
1254
+ * @param callable|string $callback
1255
+ * @param int $options
1256
+ * @param bool $descending
1257
+ * @return static
1258
+ */
1259
+ public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
1260
+ {
1261
+ $results = [];
1262
+
1263
+ $callback = $this->valueRetriever($callback);
1264
+
1265
+ // First we will loop through the items and get the comparator from a callback
1266
+ // function which we were given. Then, we will sort the returned values and
1267
+ // and grab the corresponding values for the sorted keys from this array.
1268
+ foreach ($this->items as $key => $value) {
1269
+ $results[$key] = $callback($value, $key);
1270
+ }
1271
+
1272
+ $descending ? arsort($results, $options)
1273
+ : asort($results, $options);
1274
+
1275
+ // Once we have sorted all of the keys in the array, we will loop through them
1276
+ // and grab the corresponding model so we can set the underlying items list
1277
+ // to the sorted version. Then we'll just return the collection instance.
1278
+ foreach (array_keys($results) as $key) {
1279
+ $results[$key] = $this->items[$key];
1280
+ }
1281
+
1282
+ return new static($results);
1283
+ }
1284
+
1285
+ /**
1286
+ * Sort the collection in descending order using the given callback.
1287
+ *
1288
+ * @param callable|string $callback
1289
+ * @param int $options
1290
+ * @return static
1291
+ */
1292
+ public function sortByDesc($callback, $options = SORT_REGULAR)
1293
+ {
1294
+ return $this->sortBy($callback, $options, true);
1295
+ }
1296
+
1297
+ /**
1298
+ * Splice a portion of the underlying collection array.
1299
+ *
1300
+ * @param int $offset
1301
+ * @param int|null $length
1302
+ * @param mixed $replacement
1303
+ * @return static
1304
+ */
1305
+ public function splice($offset, $length = null, $replacement = [])
1306
+ {
1307
+ if (func_num_args() == 1) {
1308
+ return new static(array_splice($this->items, $offset));
1309
+ }
1310
+
1311
+ return new static(array_splice($this->items, $offset, $length, $replacement));
1312
+ }
1313
+
1314
+ /**
1315
+ * Get the sum of the given values.
1316
+ *
1317
+ * @param callable|string|null $callback
1318
+ * @return mixed
1319
+ */
1320
+ public function sum($callback = null)
1321
+ {
1322
+ if (is_null($callback)) {
1323
+ return array_sum($this->items);
1324
+ }
1325
+
1326
+ $callback = $this->valueRetriever($callback);
1327
+
1328
+ return $this->reduce(function ($result, $item) use ($callback) {
1329
+ return $result + $callback($item);
1330
+ }, 0);
1331
+ }
1332
+
1333
+ /**
1334
+ * Take the first or last {$limit} items.
1335
+ *
1336
+ * @param int $limit
1337
+ * @return static
1338
+ */
1339
+ public function take($limit)
1340
+ {
1341
+ if ($limit < 0) {
1342
+ return $this->slice($limit, abs($limit));
1343
+ }
1344
+
1345
+ return $this->slice(0, $limit);
1346
+ }
1347
+
1348
+ /**
1349
+ * Pass the collection to the given callback and then return it.
1350
+ *
1351
+ * @param callable $callback
1352
+ * @return $this
1353
+ */
1354
+ public function tap(callable $callback)
1355
+ {
1356
+ $callback(new static($this->items));
1357
+
1358
+ return $this;
1359
+ }
1360
+
1361
+ /**
1362
+ * Transform each item in the collection using a callback.
1363
+ *
1364
+ * @param callable $callback
1365
+ * @return $this
1366
+ */
1367
+ public function transform(callable $callback)
1368
+ {
1369
+ $this->items = $this->map($callback)->all();
1370
+
1371
+ return $this;
1372
+ }
1373
+
1374
+ /**
1375
+ * Return only unique items from the collection array.
1376
+ *
1377
+ * @param string|callable|null $key
1378
+ * @param bool $strict
1379
+ * @return static
1380
+ */
1381
+ public function unique($key = null, $strict = false)
1382
+ {
1383
+ if (is_null($key)) {
1384
+ return new static(array_unique($this->items, SORT_REGULAR));
1385
+ }
1386
+
1387
+ $callback = $this->valueRetriever($key);
1388
+
1389
+ $exists = [];
1390
+
1391
+ return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) {
1392
+ if (in_array($id = $callback($item, $key), $exists, $strict)) {
1393
+ return true;
1394
+ }
1395
+
1396
+ $exists[] = $id;
1397
+ });
1398
+ }
1399
+
1400
+ /**
1401
+ * Return only unique items from the collection array using strict comparison.
1402
+ *
1403
+ * @param string|callable|null $key
1404
+ * @return static
1405
+ */
1406
+ public function uniqueStrict($key = null)
1407
+ {
1408
+ return $this->unique($key, true);
1409
+ }
1410
+
1411
+ /**
1412
+ * Reset the keys on the underlying array.
1413
+ *
1414
+ * @return static
1415
+ */
1416
+ public function values()
1417
+ {
1418
+ return new static(array_values($this->items));
1419
+ }
1420
+
1421
+ /**
1422
+ * Get a value retrieving callback.
1423
+ *
1424
+ * @param string $value
1425
+ * @return callable
1426
+ */
1427
+ protected function valueRetriever($value)
1428
+ {
1429
+ if ($this->useAsCallable($value)) {
1430
+ return $value;
1431
+ }
1432
+
1433
+ return function ($item) use ($value) {
1434
+ return data_get($item, $value);
1435
+ };
1436
+ }
1437
+
1438
+ /**
1439
+ * Zip the collection together with one or more arrays.
1440
+ *
1441
+ * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]);
1442
+ * => [[1, 4], [2, 5], [3, 6]]
1443
+ *
1444
+ * @param mixed ...$items
1445
+ * @return static
1446
+ */
1447
+ public function zip($items)
1448
+ {
1449
+ $arrayableItems = array_map(function ($items) {
1450
+ return $this->getArrayableItems($items);
1451
+ }, func_get_args());
1452
+
1453
+ $params = array_merge([function () {
1454
+ return new static(func_get_args());
1455
+ }, $this->items], $arrayableItems);
1456
+
1457
+ return new static(call_user_func_array('array_map', $params));
1458
+ }
1459
+
1460
+ /**
1461
+ * Get the collection of items as a plain array.
1462
+ *
1463
+ * @return array
1464
+ */
1465
+ public function toArray()
1466
+ {
1467
+ return array_map(function ($value) {
1468
+ return $value instanceof Arrayable ? $value->toArray() : $value;
1469
+ }, $this->items);
1470
+ }
1471
+
1472
+ /**
1473
+ * Convert the object into something JSON serializable.
1474
+ *
1475
+ * @return array
1476
+ */
1477
+ public function jsonSerialize()
1478
+ {
1479
+ return array_map(function ($value) {
1480
+ if ($value instanceof JsonSerializable) {
1481
+ return $value->jsonSerialize();
1482
+ } elseif ($value instanceof Jsonable) {
1483
+ return json_decode($value->toJson(), true);
1484
+ } elseif ($value instanceof Arrayable) {
1485
+ return $value->toArray();
1486
+ } else {
1487
+ return $value;
1488
+ }
1489
+ }, $this->items);
1490
+ }
1491
+
1492
+ /**
1493
+ * Get the collection of items as JSON.
1494
+ *
1495
+ * @param int $options
1496
+ * @return string
1497
+ */
1498
+ public function toJson($options = 0)
1499
+ {
1500
+ return json_encode($this->jsonSerialize(), $options);
1501
+ }
1502
+
1503
+ /**
1504
+ * Get an iterator for the items.
1505
+ *
1506
+ * @return \ArrayIterator
1507
+ */
1508
+ public function getIterator()
1509
+ {
1510
+ return new ArrayIterator($this->items);
1511
+ }
1512
+
1513
+ /**
1514
+ * Get a CachingIterator instance.
1515
+ *
1516
+ * @param int $flags
1517
+ * @return \CachingIterator
1518
+ */
1519
+ public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
1520
+ {
1521
+ return new CachingIterator($this->getIterator(), $flags);
1522
+ }
1523
+
1524
+ /**
1525
+ * Count the number of items in the collection.
1526
+ *
1527
+ * @return int
1528
+ */
1529
+ public function count()
1530
+ {
1531
+ return count($this->items);
1532
+ }
1533
+
1534
+ /**
1535
+ * Get a base Support collection instance from this collection.
1536
+ *
1537
+ * @return \Illuminate\Support\Collection
1538
+ */
1539
+ public function toBase()
1540
+ {
1541
+ return new self($this);
1542
+ }
1543
+
1544
+ /**
1545
+ * Determine if an item exists at an offset.
1546
+ *
1547
+ * @param mixed $key
1548
+ * @return bool
1549
+ */
1550
+ public function offsetExists($key)
1551
+ {
1552
+ return array_key_exists($key, $this->items);
1553
+ }
1554
+
1555
+ /**
1556
+ * Get an item at a given offset.
1557
+ *
1558
+ * @param mixed $key
1559
+ * @return mixed
1560
+ */
1561
+ public function offsetGet($key)
1562
+ {
1563
+ return $this->items[$key];
1564
+ }
1565
+
1566
+ /**
1567
+ * Set the item at a given offset.
1568
+ *
1569
+ * @param mixed $key
1570
+ * @param mixed $value
1571
+ * @return void
1572
+ */
1573
+ public function offsetSet($key, $value)
1574
+ {
1575
+ if (is_null($key)) {
1576
+ $this->items[] = $value;
1577
+ } else {
1578
+ $this->items[$key] = $value;
1579
+ }
1580
+ }
1581
+
1582
+ /**
1583
+ * Unset the item at a given offset.
1584
+ *
1585
+ * @param string $key
1586
+ * @return void
1587
+ */
1588
+ public function offsetUnset($key)
1589
+ {
1590
+ unset($this->items[$key]);
1591
+ }
1592
+
1593
+ /**
1594
+ * Convert the collection to its string representation.
1595
+ *
1596
+ * @return string
1597
+ */
1598
+ public function __toString()
1599
+ {
1600
+ return $this->toJson();
1601
+ }
1602
+
1603
+ /**
1604
+ * Results array of items from Collection or Arrayable.
1605
+ *
1606
+ * @param mixed $items
1607
+ * @return array
1608
+ */
1609
+ protected function getArrayableItems($items)
1610
+ {
1611
+ if (is_array($items)) {
1612
+ return $items;
1613
+ } elseif ($items instanceof self) {
1614
+ return $items->all();
1615
+ } elseif ($items instanceof Arrayable) {
1616
+ return $items->toArray();
1617
+ } elseif ($items instanceof Jsonable) {
1618
+ return json_decode($items->toJson(), true);
1619
+ } elseif ($items instanceof JsonSerializable) {
1620
+ return $items->jsonSerialize();
1621
+ } elseif ($items instanceof Traversable) {
1622
+ return iterator_to_array($items);
1623
+ }
1624
+
1625
+ return (array) $items;
1626
+ }
1627
+
1628
+ /**
1629
+ * Add a method to the list of proxied methods.
1630
+ *
1631
+ * @param string $method
1632
+ * @return void
1633
+ */
1634
+ public static function proxy($method)
1635
+ {
1636
+ static::$proxies[] = $method;
1637
+ }
1638
+
1639
+ /**
1640
+ * Dynamically access collection proxies.
1641
+ *
1642
+ * @param string $key
1643
+ * @return mixed
1644
+ *
1645
+ * @throws \Exception
1646
+ */
1647
+ public function __get($key)
1648
+ {
1649
+ if (! in_array($key, static::$proxies)) {
1650
+ throw new Exception("Property [{$key}] does not exist on this collection instance.");
1651
+ }
1652
+
1653
+ return new HigherOrderCollectionProxy($this, $key);
1654
+ }
1655
+ }
vendor/illuminate/support/Composer.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support;
4
+
5
+ use Illuminate\Filesystem\Filesystem;
6
+ use Symfony\Component\Process\Process;
7
+ use Symfony\Component\Process\ProcessUtils;
8
+ use Symfony\Component\Process\PhpExecutableFinder;
9
+
10
+ class Composer
11
+ {
12
+ /**
13
+ * The filesystem instance.
14
+ *
15
+ * @var \Illuminate\Filesystem\Filesystem
16
+ */
17
+ protected $files;
18
+
19
+ /**
20
+ * The working path to regenerate from.
21
+ *
22
+ * @var string
23
+ */
24
+ protected $workingPath;
25
+
26
+ /**
27
+ * Create a new Composer manager instance.
28
+ *
29
+ * @param \Illuminate\Filesystem\Filesystem $files
30
+ * @param string|null $workingPath
31
+ * @return void
32
+ */
33
+ public function __construct(Filesystem $files, $workingPath = null)
34
+ {
35
+ $this->files = $files;
36
+ $this->workingPath = $workingPath;
37
+ }
38
+
39
+ /**
40
+ * Regenerate the Composer autoloader files.
41
+ *
42
+ * @param string $extra
43
+ * @return void
44
+ */
45
+ public function dumpAutoloads($extra = '')
46
+ {
47
+ $process = $this->getProcess();
48
+
49
+ $process->setCommandLine(trim($this->findComposer().' dump-autoload '.$extra));
50
+
51
+ $process->run();
52
+ }
53
+
54
+ /**
55
+ * Regenerate the optimized Composer autoloader files.
56
+ *
57
+ * @return void
58
+ */
59
+ public function dumpOptimized()
60
+ {
61
+ $this->dumpAutoloads('--optimize');
62
+ }
63
+
64
+ /**
65
+ * Get the composer command for the environment.
66
+ *
67
+ * @return string
68
+ */
69
+ protected function findComposer()
70
+ {
71
+ if ($this->files->exists($this->workingPath.'/composer.phar')) {
72
+ return ProcessUtils::escapeArgument((new PhpExecutableFinder)->find(false)).' composer.phar';
73
+ }
74
+
75
+ return 'composer';
76
+ }
77
+
78
+ /**
79
+ * Get a new Symfony process instance.
80
+ *
81
+ * @return \Symfony\Component\Process\Process
82
+ */
83
+ protected function getProcess()
84
+ {
85
+ return (new Process('', $this->workingPath))->setTimeout(null);
86
+ }
87
+
88
+ /**
89
+ * Set the working path used by the class.
90
+ *
91
+ * @param string $path
92
+ * @return $this
93
+ */
94
+ public function setWorkingPath($path)
95
+ {
96
+ $this->workingPath = realpath($path);
97
+
98
+ return $this;
99
+ }
100
+ }
vendor/illuminate/support/Debug/Dumper.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Debug;
4
+
5
+ use Symfony\Component\VarDumper\Cloner\VarCloner;
6
+ use Symfony\Component\VarDumper\Dumper\CliDumper;
7
+
8
+ class Dumper
9
+ {
10
+ /**
11
+ * Dump a value with elegance.
12
+ *
13
+ * @param mixed $value
14
+ * @return void
15
+ */
16
+ public function dump($value)
17
+ {
18
+ if (class_exists(CliDumper::class)) {
19
+ $dumper = 'cli' === PHP_SAPI ? new CliDumper : new HtmlDumper;
20
+
21
+ $dumper->dump((new VarCloner)->cloneVar($value));
22
+ } else {
23
+ var_dump($value);
24
+ }
25
+ }
26
+ }
vendor/illuminate/support/Debug/HtmlDumper.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Debug;
4
+
5
+ use Symfony\Component\VarDumper\Dumper\HtmlDumper as SymfonyHtmlDumper;
6
+
7
+ class HtmlDumper extends SymfonyHtmlDumper
8
+ {
9
+ /**
10
+ * Colour definitions for output.
11
+ *
12
+ * @var array
13
+ */
14
+ protected $styles = [
15
+ 'default' => 'background-color:#fff; color:#222; line-height:1.2em; font-weight:normal; font:12px Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:100000',
16
+ 'num' => 'color:#a71d5d',
17
+ 'const' => 'color:#795da3',
18
+ 'str' => 'color:#df5000',
19
+ 'cchr' => 'color:#222',
20
+ 'note' => 'color:#a71d5d',
21
+ 'ref' => 'color:#a0a0a0',
22
+ 'public' => 'color:#795da3',
23
+ 'protected' => 'color:#795da3',
24
+ 'private' => 'color:#795da3',
25
+ 'meta' => 'color:#b729d9',
26
+ 'key' => 'color:#df5000',
27
+ 'index' => 'color:#a71d5d',
28
+ ];
29
+ }
vendor/illuminate/support/Facades/App.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @method static string version()
7
+ * @method static string basePath()
8
+ * @method static string environment()
9
+ * @method static bool isDownForMaintenance()
10
+ * @method static void registerConfiguredProviders()
11
+ * @method static \Illuminate\Support\ServiceProvider register(\Illuminate\Support\ServiceProvider|string $provider, array $options = [], bool $force = false)
12
+ * @method static void registerDeferredProvider(string $provider, string $service = null)
13
+ * @method static void boot()
14
+ * @method static void booting(mixed $callback)
15
+ * @method static void booted(mixed $callback)
16
+ * @method static string getCachedServicesPath()
17
+ *
18
+ * @see \Illuminate\Foundation\Application
19
+ */
20
+ class App extends Facade
21
+ {
22
+ /**
23
+ * Get the registered name of the component.
24
+ *
25
+ * @return string
26
+ */
27
+ protected static function getFacadeAccessor()
28
+ {
29
+ return 'app';
30
+ }
31
+ }
vendor/illuminate/support/Facades/Artisan.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ use Illuminate\Contracts\Console\Kernel as ConsoleKernelContract;
6
+
7
+ /**
8
+ * @method static int handle(\Symfony\Component\Console\Input\InputInterface $input, \Symfony\Component\Console\Output\OutputInterface $output = null)
9
+ * @method static int call(string $command, array $parameters = [])
10
+ * @method static int queue(string $command, array $parameters = [])
11
+ * @method static array all()
12
+ * @method static string output()
13
+ *
14
+ * @see \Illuminate\Contracts\Console\Kernel
15
+ */
16
+ class Artisan extends Facade
17
+ {
18
+ /**
19
+ * Get the registered name of the component.
20
+ *
21
+ * @return string
22
+ */
23
+ protected static function getFacadeAccessor()
24
+ {
25
+ return ConsoleKernelContract::class;
26
+ }
27
+ }
vendor/illuminate/support/Facades/Auth.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @method static mixed guard(string|null $name = null)
7
+ * @method static void shouldUse(string $name);
8
+ * @method static bool check()
9
+ * @method static bool guest()
10
+ * @method static \Illuminate\Contracts\Auth\Authenticatable|null user()
11
+ * @method static int|null id()
12
+ * @method static bool validate(array $credentials = [])
13
+ * @method static void setUser(\Illuminate\Contracts\Auth\Authenticatable $user)
14
+ * @method static bool attempt(array $credentials = [], bool $remember = false)
15
+ * @method static bool once(array $credentials = [])
16
+ * @method static void login(\Illuminate\Contracts\Auth\Authenticatable $user, bool $remember = false)
17
+ * @method static \Illuminate\Contracts\Auth\Authenticatable loginUsingId(mixed $id, bool $remember = false)
18
+ * @method static bool onceUsingId(mixed $id)
19
+ * @method static bool viaRemember()
20
+ * @method static void logout()
21
+ *
22
+ * @see \Illuminate\Auth\AuthManager
23
+ * @see \Illuminate\Contracts\Auth\Factory
24
+ * @see \Illuminate\Contracts\Auth\Guard
25
+ * @see \Illuminate\Contracts\Auth\StatefulGuard
26
+ */
27
+ class Auth extends Facade
28
+ {
29
+ /**
30
+ * Get the registered name of the component.
31
+ *
32
+ * @return string
33
+ */
34
+ protected static function getFacadeAccessor()
35
+ {
36
+ return 'auth';
37
+ }
38
+
39
+ /**
40
+ * Register the typical authentication routes for an application.
41
+ *
42
+ * @return void
43
+ */
44
+ public static function routes()
45
+ {
46
+ static::$app->make('router')->auth();
47
+ }
48
+ }
vendor/illuminate/support/Facades/Blade.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\View\Compilers\BladeCompiler
7
+ */
8
+ class Blade extends Facade
9
+ {
10
+ /**
11
+ * Get the registered name of the component.
12
+ *
13
+ * @return string
14
+ */
15
+ protected static function getFacadeAccessor()
16
+ {
17
+ return static::$app['view']->getEngineResolver()->resolve('blade')->getCompiler();
18
+ }
19
+ }
vendor/illuminate/support/Facades/Broadcast.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ use Illuminate\Contracts\Broadcasting\Factory as BroadcastingFactoryContract;
6
+
7
+ /**
8
+ * @see \Illuminate\Contracts\Broadcasting\Factory
9
+ */
10
+ class Broadcast extends Facade
11
+ {
12
+ /**
13
+ * Get the registered name of the component.
14
+ *
15
+ * @return string
16
+ */
17
+ protected static function getFacadeAccessor()
18
+ {
19
+ return BroadcastingFactoryContract::class;
20
+ }
21
+ }
vendor/illuminate/support/Facades/Bus.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ use Illuminate\Support\Testing\Fakes\BusFake;
6
+ use Illuminate\Contracts\Bus\Dispatcher as BusDispatcherContract;
7
+
8
+ /**
9
+ * @see \Illuminate\Contracts\Bus\Dispatcher
10
+ */
11
+ class Bus extends Facade
12
+ {
13
+ /**
14
+ * Replace the bound instance with a fake.
15
+ *
16
+ * @return void
17
+ */
18
+ public static function fake()
19
+ {
20
+ static::swap(new BusFake);
21
+ }
22
+
23
+ /**
24
+ * Get the registered name of the component.
25
+ *
26
+ * @return string
27
+ */
28
+ protected static function getFacadeAccessor()
29
+ {
30
+ return BusDispatcherContract::class;
31
+ }
32
+ }
vendor/illuminate/support/Facades/Cache.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Cache\CacheManager
7
+ * @see \Illuminate\Cache\Repository
8
+ */
9
+ class Cache extends Facade
10
+ {
11
+ /**
12
+ * Get the registered name of the component.
13
+ *
14
+ * @return string
15
+ */
16
+ protected static function getFacadeAccessor()
17
+ {
18
+ return 'cache';
19
+ }
20
+ }
vendor/illuminate/support/Facades/Config.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Config\Repository
7
+ */
8
+ class Config extends Facade
9
+ {
10
+ /**
11
+ * Get the registered name of the component.
12
+ *
13
+ * @return string
14
+ */
15
+ protected static function getFacadeAccessor()
16
+ {
17
+ return 'config';
18
+ }
19
+ }
vendor/illuminate/support/Facades/Cookie.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Cookie\CookieJar
7
+ */
8
+ class Cookie extends Facade
9
+ {
10
+ /**
11
+ * Determine if a cookie exists on the request.
12
+ *
13
+ * @param string $key
14
+ * @return bool
15
+ */
16
+ public static function has($key)
17
+ {
18
+ return ! is_null(static::$app['request']->cookie($key, null));
19
+ }
20
+
21
+ /**
22
+ * Retrieve a cookie from the request.
23
+ *
24
+ * @param string $key
25
+ * @param mixed $default
26
+ * @return string
27
+ */
28
+ public static function get($key = null, $default = null)
29
+ {
30
+ return static::$app['request']->cookie($key, $default);
31
+ }
32
+
33
+ /**
34
+ * Get the registered name of the component.
35
+ *
36
+ * @return string
37
+ */
38
+ protected static function getFacadeAccessor()
39
+ {
40
+ return 'cookie';
41
+ }
42
+ }
vendor/illuminate/support/Facades/Crypt.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Encryption\Encrypter
7
+ */
8
+ class Crypt extends Facade
9
+ {
10
+ /**
11
+ * Get the registered name of the component.
12
+ *
13
+ * @return string
14
+ */
15
+ protected static function getFacadeAccessor()
16
+ {
17
+ return 'encrypter';
18
+ }
19
+ }
vendor/illuminate/support/Facades/DB.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Database\DatabaseManager
7
+ * @see \Illuminate\Database\Connection
8
+ */
9
+ class DB extends Facade
10
+ {
11
+ /**
12
+ * Get the registered name of the component.
13
+ *
14
+ * @return string
15
+ */
16
+ protected static function getFacadeAccessor()
17
+ {
18
+ return 'db';
19
+ }
20
+ }
vendor/illuminate/support/Facades/Event.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ use Illuminate\Database\Eloquent\Model;
6
+ use Illuminate\Support\Testing\Fakes\EventFake;
7
+
8
+ /**
9
+ * @see \Illuminate\Events\Dispatcher
10
+ */
11
+ class Event extends Facade
12
+ {
13
+ /**
14
+ * Replace the bound instance with a fake.
15
+ *
16
+ * @return void
17
+ */
18
+ public static function fake()
19
+ {
20
+ static::swap($fake = new EventFake);
21
+
22
+ Model::setEventDispatcher($fake);
23
+ }
24
+
25
+ /**
26
+ * Get the registered name of the component.
27
+ *
28
+ * @return string
29
+ */
30
+ protected static function getFacadeAccessor()
31
+ {
32
+ return 'events';
33
+ }
34
+ }
vendor/illuminate/support/Facades/Facade.php ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ use Mockery;
6
+ use RuntimeException;
7
+ use Mockery\MockInterface;
8
+
9
+ abstract class Facade
10
+ {
11
+ /**
12
+ * The application instance being facaded.
13
+ *
14
+ * @var \Illuminate\Contracts\Foundation\Application
15
+ */
16
+ protected static $app;
17
+
18
+ /**
19
+ * The resolved object instances.
20
+ *
21
+ * @var array
22
+ */
23
+ protected static $resolvedInstance;
24
+
25
+ /**
26
+ * Convert the facade into a Mockery spy.
27
+ *
28
+ * @return void
29
+ */
30
+ public static function spy()
31
+ {
32
+ if (! static::isMock()) {
33
+ $class = static::getMockableClass();
34
+
35
+ static::swap($class ? Mockery::spy($class) : Mockery::spy());
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Initiate a mock expectation on the facade.
41
+ *
42
+ * @return \Mockery\Expectation
43
+ */
44
+ public static function shouldReceive()
45
+ {
46
+ $name = static::getFacadeAccessor();
47
+
48
+ $mock = static::isMock()
49
+ ? static::$resolvedInstance[$name]
50
+ : static::createFreshMockInstance();
51
+
52
+ return $mock->shouldReceive(...func_get_args());
53
+ }
54
+
55
+ /**
56
+ * Create a fresh mock instance for the given class.
57
+ *
58
+ * @return \Mockery\Expectation
59
+ */
60
+ protected static function createFreshMockInstance()
61
+ {
62
+ return tap(static::createMock(), function ($mock) {
63
+ static::swap($mock);
64
+
65
+ $mock->shouldAllowMockingProtectedMethods();
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Create a fresh mock instance for the given class.
71
+ *
72
+ * @return \Mockery\MockInterface
73
+ */
74
+ protected static function createMock()
75
+ {
76
+ $class = static::getMockableClass();
77
+
78
+ return $class ? Mockery::mock($class) : Mockery::mock();
79
+ }
80
+
81
+ /**
82
+ * Determines whether a mock is set as the instance of the facade.
83
+ *
84
+ * @return bool
85
+ */
86
+ protected static function isMock()
87
+ {
88
+ $name = static::getFacadeAccessor();
89
+
90
+ return isset(static::$resolvedInstance[$name]) &&
91
+ static::$resolvedInstance[$name] instanceof MockInterface;
92
+ }
93
+
94
+ /**
95
+ * Get the mockable class for the bound instance.
96
+ *
97
+ * @return string|null
98
+ */
99
+ protected static function getMockableClass()
100
+ {
101
+ if ($root = static::getFacadeRoot()) {
102
+ return get_class($root);
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Hotswap the underlying instance behind the facade.
108
+ *
109
+ * @param mixed $instance
110
+ * @return void
111
+ */
112
+ public static function swap($instance)
113
+ {
114
+ static::$resolvedInstance[static::getFacadeAccessor()] = $instance;
115
+
116
+ if (isset(static::$app)) {
117
+ static::$app->instance(static::getFacadeAccessor(), $instance);
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Get the root object behind the facade.
123
+ *
124
+ * @return mixed
125
+ */
126
+ public static function getFacadeRoot()
127
+ {
128
+ return static::resolveFacadeInstance(static::getFacadeAccessor());
129
+ }
130
+
131
+ /**
132
+ * Get the registered name of the component.
133
+ *
134
+ * @return string
135
+ *
136
+ * @throws \RuntimeException
137
+ */
138
+ protected static function getFacadeAccessor()
139
+ {
140
+ throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
141
+ }
142
+
143
+ /**
144
+ * Resolve the facade root instance from the container.
145
+ *
146
+ * @param string|object $name
147
+ * @return mixed
148
+ */
149
+ protected static function resolveFacadeInstance($name)
150
+ {
151
+ if (is_object($name)) {
152
+ return $name;
153
+ }
154
+
155
+ if (isset(static::$resolvedInstance[$name])) {
156
+ return static::$resolvedInstance[$name];
157
+ }
158
+
159
+ return static::$resolvedInstance[$name] = static::$app[$name];
160
+ }
161
+
162
+ /**
163
+ * Clear a resolved facade instance.
164
+ *
165
+ * @param string $name
166
+ * @return void
167
+ */
168
+ public static function clearResolvedInstance($name)
169
+ {
170
+ unset(static::$resolvedInstance[$name]);
171
+ }
172
+
173
+ /**
174
+ * Clear all of the resolved instances.
175
+ *
176
+ * @return void
177
+ */
178
+ public static function clearResolvedInstances()
179
+ {
180
+ static::$resolvedInstance = [];
181
+ }
182
+
183
+ /**
184
+ * Get the application instance behind the facade.
185
+ *
186
+ * @return \Illuminate\Contracts\Foundation\Application
187
+ */
188
+ public static function getFacadeApplication()
189
+ {
190
+ return static::$app;
191
+ }
192
+
193
+ /**
194
+ * Set the application instance.
195
+ *
196
+ * @param \Illuminate\Contracts\Foundation\Application $app
197
+ * @return void
198
+ */
199
+ public static function setFacadeApplication($app)
200
+ {
201
+ static::$app = $app;
202
+ }
203
+
204
+ /**
205
+ * Handle dynamic, static calls to the object.
206
+ *
207
+ * @param string $method
208
+ * @param array $args
209
+ * @return mixed
210
+ *
211
+ * @throws \RuntimeException
212
+ */
213
+ public static function __callStatic($method, $args)
214
+ {
215
+ $instance = static::getFacadeRoot();
216
+
217
+ if (! $instance) {
218
+ throw new RuntimeException('A facade root has not been set.');
219
+ }
220
+
221
+ return $instance->$method(...$args);
222
+ }
223
+ }
vendor/illuminate/support/Facades/File.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Filesystem\Filesystem
7
+ */
8
+ class File extends Facade
9
+ {
10
+ /**
11
+ * Get the registered name of the component.
12
+ *
13
+ * @return string
14
+ */
15
+ protected static function getFacadeAccessor()
16
+ {
17
+ return 'files';
18
+ }
19
+ }
vendor/illuminate/support/Facades/Gate.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ use Illuminate\Contracts\Auth\Access\Gate as GateContract;
6
+
7
+ /**
8
+ * @see \Illuminate\Contracts\Auth\Access\Gate
9
+ */
10
+ class Gate extends Facade
11
+ {
12
+ /**
13
+ * Get the registered name of the component.
14
+ *
15
+ * @return string
16
+ */
17
+ protected static function getFacadeAccessor()
18
+ {
19
+ return GateContract::class;
20
+ }
21
+ }
vendor/illuminate/support/Facades/Hash.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Hashing\BcryptHasher
7
+ */
8
+ class Hash extends Facade
9
+ {
10
+ /**
11
+ * Get the registered name of the component.
12
+ *
13
+ * @return string
14
+ */
15
+ protected static function getFacadeAccessor()
16
+ {
17
+ return 'hash';
18
+ }
19
+ }
vendor/illuminate/support/Facades/Input.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Http\Request
7
+ */
8
+ class Input extends Facade
9
+ {
10
+ /**
11
+ * Get an item from the input data.
12
+ *
13
+ * This method is used for all request verbs (GET, POST, PUT, and DELETE)
14
+ *
15
+ * @param string $key
16
+ * @param mixed $default
17
+ * @return mixed
18
+ */
19
+ public static function get($key = null, $default = null)
20
+ {
21
+ return static::$app['request']->input($key, $default);
22
+ }
23
+
24
+ /**
25
+ * Get the registered name of the component.
26
+ *
27
+ * @return string
28
+ */
29
+ protected static function getFacadeAccessor()
30
+ {
31
+ return 'request';
32
+ }
33
+ }
vendor/illuminate/support/Facades/Lang.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Translation\Translator
7
+ */
8
+ class Lang extends Facade
9
+ {
10
+ /**
11
+ * Get the registered name of the component.
12
+ *
13
+ * @return string
14
+ */
15
+ protected static function getFacadeAccessor()
16
+ {
17
+ return 'translator';
18
+ }
19
+ }
vendor/illuminate/support/Facades/Log.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ use Psr\Log\LoggerInterface;
6
+
7
+ /**
8
+ * @see \Illuminate\Log\Writer
9
+ */
10
+ class Log extends Facade
11
+ {
12
+ /**
13
+ * Get the registered name of the component.
14
+ *
15
+ * @return string
16
+ */
17
+ protected static function getFacadeAccessor()
18
+ {
19
+ return LoggerInterface::class;
20
+ }
21
+ }
vendor/illuminate/support/Facades/Mail.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ use Illuminate\Support\Testing\Fakes\MailFake;
6
+
7
+ /**
8
+ * @see \Illuminate\Mail\Mailer
9
+ */
10
+ class Mail extends Facade
11
+ {
12
+ /**
13
+ * Replace the bound instance with a fake.
14
+ *
15
+ * @return void
16
+ */
17
+ public static function fake()
18
+ {
19
+ static::swap(new MailFake);
20
+ }
21
+
22
+ /**
23
+ * Get the registered name of the component.
24
+ *
25
+ * @return string
26
+ */
27
+ protected static function getFacadeAccessor()
28
+ {
29
+ return 'mailer';
30
+ }
31
+ }
vendor/illuminate/support/Facades/Notification.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ use Illuminate\Notifications\ChannelManager;
6
+ use Illuminate\Support\Testing\Fakes\NotificationFake;
7
+
8
+ /**
9
+ * @see \Illuminate\Notifications\ChannelManager
10
+ */
11
+ class Notification extends Facade
12
+ {
13
+ /**
14
+ * Replace the bound instance with a fake.
15
+ *
16
+ * @return void
17
+ */
18
+ public static function fake()
19
+ {
20
+ static::swap(new NotificationFake);
21
+ }
22
+
23
+ /**
24
+ * Get the registered name of the component.
25
+ *
26
+ * @return string
27
+ */
28
+ protected static function getFacadeAccessor()
29
+ {
30
+ return ChannelManager::class;
31
+ }
32
+ }
vendor/illuminate/support/Facades/Password.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Auth\Passwords\PasswordBroker
7
+ */
8
+ class Password extends Facade
9
+ {
10
+ /**
11
+ * Constant representing a successfully sent reminder.
12
+ *
13
+ * @var string
14
+ */
15
+ const RESET_LINK_SENT = 'passwords.sent';
16
+
17
+ /**
18
+ * Constant representing a successfully reset password.
19
+ *
20
+ * @var string
21
+ */
22
+ const PASSWORD_RESET = 'passwords.reset';
23
+
24
+ /**
25
+ * Constant representing the user not found response.
26
+ *
27
+ * @var string
28
+ */
29
+ const INVALID_USER = 'passwords.user';
30
+
31
+ /**
32
+ * Constant representing an invalid password.
33
+ *
34
+ * @var string
35
+ */
36
+ const INVALID_PASSWORD = 'passwords.password';
37
+
38
+ /**
39
+ * Constant representing an invalid token.
40
+ *
41
+ * @var string
42
+ */
43
+ const INVALID_TOKEN = 'passwords.token';
44
+
45
+ /**
46
+ * Get the registered name of the component.
47
+ *
48
+ * @return string
49
+ */
50
+ protected static function getFacadeAccessor()
51
+ {
52
+ return 'auth.password';
53
+ }
54
+ }
vendor/illuminate/support/Facades/Queue.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ use Illuminate\Support\Testing\Fakes\QueueFake;
6
+
7
+ /**
8
+ * @see \Illuminate\Queue\QueueManager
9
+ * @see \Illuminate\Queue\Queue
10
+ */
11
+ class Queue extends Facade
12
+ {
13
+ /**
14
+ * Replace the bound instance with a fake.
15
+ *
16
+ * @return void
17
+ */
18
+ public static function fake()
19
+ {
20
+ static::swap(new QueueFake(static::getFacadeApplication()));
21
+ }
22
+
23
+ /**
24
+ * Get the registered name of the component.
25
+ *
26
+ * @return string
27
+ */
28
+ protected static function getFacadeAccessor()
29
+ {
30
+ return 'queue';
31
+ }
32
+ }
vendor/illuminate/support/Facades/Redirect.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Routing\Redirector
7
+ */
8
+ class Redirect extends Facade
9
+ {
10
+ /**
11
+ * Get the registered name of the component.
12
+ *
13
+ * @return string
14
+ */
15
+ protected static function getFacadeAccessor()
16
+ {
17
+ return 'redirect';
18
+ }
19
+ }
vendor/illuminate/support/Facades/Redis.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Redis\RedisManager
7
+ * @see \Illuminate\Contracts\Redis\Factory
8
+ */
9
+ class Redis extends Facade
10
+ {
11
+ /**
12
+ * Get the registered name of the component.
13
+ *
14
+ * @return string
15
+ */
16
+ protected static function getFacadeAccessor()
17
+ {
18
+ return 'redis';
19
+ }
20
+ }
vendor/illuminate/support/Facades/Request.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Http\Request
7
+ */
8
+ class Request extends Facade
9
+ {
10
+ /**
11
+ * Get the registered name of the component.
12
+ *
13
+ * @return string
14
+ */
15
+ protected static function getFacadeAccessor()
16
+ {
17
+ return 'request';
18
+ }
19
+ }
vendor/illuminate/support/Facades/Response.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ use Illuminate\Contracts\Routing\ResponseFactory as ResponseFactoryContract;
6
+
7
+ /**
8
+ * @see \Illuminate\Contracts\Routing\ResponseFactory
9
+ */
10
+ class Response extends Facade
11
+ {
12
+ /**
13
+ * Get the registered name of the component.
14
+ *
15
+ * @return string
16
+ */
17
+ protected static function getFacadeAccessor()
18
+ {
19
+ return ResponseFactoryContract::class;
20
+ }
21
+ }
vendor/illuminate/support/Facades/Route.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @method static \Illuminate\Routing\Route get(string $uri, \Closure|array|string|null $action = null)
7
+ * @method static \Illuminate\Routing\Route post(string $uri, \Closure|array|string|null $action = null)
8
+ * @method static \Illuminate\Routing\Route put(string $uri, \Closure|array|string|null $action = null)
9
+ * @method static \Illuminate\Routing\Route delete(string $uri, \Closure|array|string|null $action = null)
10
+ * @method static \Illuminate\Routing\Route patch(string $uri, \Closure|array|string|null $action = null)
11
+ * @method static \Illuminate\Routing\Route options(string $uri, \Closure|array|string|null $action = null)
12
+ * @method static \Illuminate\Routing\Route any(string $uri, \Closure|array|string|null $action = null)
13
+ * @method static \Illuminate\Routing\Route match(array|string $methods, string $uri, \Closure|array|string|null $action = null)
14
+ * @method static \Illuminate\Routing\Route prefix(string $prefix)
15
+ * @method static void resource(string $name, string $controller, array $options = [])
16
+ * @method static void apiResource(string $name, string $controller, array $options = [])
17
+ * @method static void group(array $attributes, \Closure|string $callback)
18
+ * @method static \Illuminate\Routing\Route middleware(array|string|null $middleware)
19
+ * @method static \Illuminate\Routing\Route substituteBindings(\Illuminate\Routing\Route $route)
20
+ * @method static void substituteImplicitBindings(\Illuminate\Routing\Route $route)
21
+ *
22
+ * @see \Illuminate\Routing\Router
23
+ */
24
+ class Route extends Facade
25
+ {
26
+ /**
27
+ * Get the registered name of the component.
28
+ *
29
+ * @return string
30
+ */
31
+ protected static function getFacadeAccessor()
32
+ {
33
+ return 'router';
34
+ }
35
+ }
vendor/illuminate/support/Facades/Schema.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @method static \Illuminate\Database\Schema\Builder create(string $table, \Closure $callback)
7
+ * @method static \Illuminate\Database\Schema\Builder drop(string $table)
8
+ * @method static \Illuminate\Database\Schema\Builder dropIfExists(string $table)
9
+ * @method static \Illuminate\Database\Schema\Builder table(string $table, \Closure $callback)
10
+ *
11
+ * @see \Illuminate\Database\Schema\Builder
12
+ */
13
+ class Schema extends Facade
14
+ {
15
+ /**
16
+ * Get a schema builder instance for a connection.
17
+ *
18
+ * @param string $name
19
+ * @return \Illuminate\Database\Schema\Builder
20
+ */
21
+ public static function connection($name)
22
+ {
23
+ return static::$app['db']->connection($name)->getSchemaBuilder();
24
+ }
25
+
26
+ /**
27
+ * Get a schema builder instance for the default connection.
28
+ *
29
+ * @return \Illuminate\Database\Schema\Builder
30
+ */
31
+ protected static function getFacadeAccessor()
32
+ {
33
+ return static::$app['db']->connection()->getSchemaBuilder();
34
+ }
35
+ }
vendor/illuminate/support/Facades/Session.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Session\SessionManager
7
+ * @see \Illuminate\Session\Store
8
+ */
9
+ class Session extends Facade
10
+ {
11
+ /**
12
+ * Get the registered name of the component.
13
+ *
14
+ * @return string
15
+ */
16
+ protected static function getFacadeAccessor()
17
+ {
18
+ return 'session';
19
+ }
20
+ }
vendor/illuminate/support/Facades/Storage.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ use Illuminate\Filesystem\Filesystem;
6
+
7
+ /**
8
+ * @see \Illuminate\Filesystem\FilesystemManager
9
+ */
10
+ class Storage extends Facade
11
+ {
12
+ /**
13
+ * Replace the given disk with a local testing disk.
14
+ *
15
+ * @param string $disk
16
+ *
17
+ * @return void
18
+ */
19
+ public static function fake($disk)
20
+ {
21
+ (new Filesystem)->cleanDirectory(
22
+ $root = storage_path('framework/testing/disks/'.$disk)
23
+ );
24
+
25
+ static::set($disk, self::createLocalDriver(['root' => $root]));
26
+ }
27
+
28
+ /**
29
+ * Replace the given disk with a persistent local testing disk.
30
+ *
31
+ * @param string $disk
32
+ * @return void
33
+ */
34
+ public static function persistentFake($disk)
35
+ {
36
+ static::set($disk, self::createLocalDriver([
37
+ 'root' => storage_path('framework/testing/disks/'.$disk),
38
+ ]));
39
+ }
40
+
41
+ /**
42
+ * Get the registered name of the component.
43
+ *
44
+ * @return string
45
+ */
46
+ protected static function getFacadeAccessor()
47
+ {
48
+ return 'filesystem';
49
+ }
50
+ }
vendor/illuminate/support/Facades/URL.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Routing\UrlGenerator
7
+ */
8
+ class URL extends Facade
9
+ {
10
+ /**
11
+ * Get the registered name of the component.
12
+ *
13
+ * @return string
14
+ */
15
+ protected static function getFacadeAccessor()
16
+ {
17
+ return 'url';
18
+ }
19
+ }
vendor/illuminate/support/Facades/Validator.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\Validation\Factory
7
+ */
8
+ class Validator extends Facade
9
+ {
10
+ /**
11
+ * Get the registered name of the component.
12
+ *
13
+ * @return string
14
+ */
15
+ protected static function getFacadeAccessor()
16
+ {
17
+ return 'validator';
18
+ }
19
+ }
vendor/illuminate/support/Facades/View.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Facades;
4
+
5
+ /**
6
+ * @see \Illuminate\View\Factory
7
+ */
8
+ class View extends Facade
9
+ {
10
+ /**
11
+ * Get the registered name of the component.
12
+ *
13
+ * @return string
14
+ */
15
+ protected static function getFacadeAccessor()
16
+ {
17
+ return 'view';
18
+ }
19
+ }
vendor/illuminate/support/Fluent.php ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support;
4
+
5
+ use ArrayAccess;
6
+ use JsonSerializable;
7
+ use Illuminate\Contracts\Support\Jsonable;
8
+ use Illuminate\Contracts\Support\Arrayable;
9
+
10
+ class Fluent implements ArrayAccess, Arrayable, Jsonable, JsonSerializable
11
+ {
12
+ /**
13
+ * All of the attributes set on the container.
14
+ *
15
+ * @var array
16
+ */
17
+ protected $attributes = [];
18
+
19
+ /**
20
+ * Create a new fluent container instance.
21
+ *
22
+ * @param array|object $attributes
23
+ * @return void
24
+ */
25
+ public function __construct($attributes = [])
26
+ {
27
+ foreach ($attributes as $key => $value) {
28
+ $this->attributes[$key] = $value;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Get an attribute from the container.
34
+ *
35
+ * @param string $key
36
+ * @param mixed $default
37
+ * @return mixed
38
+ */
39
+ public function get($key, $default = null)
40
+ {
41
+ if (array_key_exists($key, $this->attributes)) {
42
+ return $this->attributes[$key];
43
+ }
44
+
45
+ return value($default);
46
+ }
47
+
48
+ /**
49
+ * Get the attributes from the container.
50
+ *
51
+ * @return array
52
+ */
53
+ public function getAttributes()
54
+ {
55
+ return $this->attributes;
56
+ }
57
+
58
+ /**
59
+ * Convert the Fluent instance to an array.
60
+ *
61
+ * @return array
62
+ */
63
+ public function toArray()
64
+ {
65
+ return $this->attributes;
66
+ }
67
+
68
+ /**
69
+ * Convert the object into something JSON serializable.
70
+ *
71
+ * @return array
72
+ */
73
+ public function jsonSerialize()
74
+ {
75
+ return $this->toArray();
76
+ }
77
+
78
+ /**
79
+ * Convert the Fluent instance to JSON.
80
+ *
81
+ * @param int $options
82
+ * @return string
83
+ */
84
+ public function toJson($options = 0)
85
+ {
86
+ return json_encode($this->jsonSerialize(), $options);
87
+ }
88
+
89
+ /**
90
+ * Determine if the given offset exists.
91
+ *
92
+ * @param string $offset
93
+ * @return bool
94
+ */
95
+ public function offsetExists($offset)
96
+ {
97
+ return isset($this->{$offset});
98
+ }
99
+
100
+ /**
101
+ * Get the value for a given offset.
102
+ *
103
+ * @param string $offset
104
+ * @return mixed
105
+ */
106
+ public function offsetGet($offset)
107
+ {
108
+ return $this->{$offset};
109
+ }
110
+
111
+ /**
112
+ * Set the value at the given offset.
113
+ *
114
+ * @param string $offset
115
+ * @param mixed $value
116
+ * @return void
117
+ */
118
+ public function offsetSet($offset, $value)
119
+ {
120
+ $this->{$offset} = $value;
121
+ }
122
+
123
+ /**
124
+ * Unset the value at the given offset.
125
+ *
126
+ * @param string $offset
127
+ * @return void
128
+ */
129
+ public function offsetUnset($offset)
130
+ {
131
+ unset($this->{$offset});
132
+ }
133
+
134
+ /**
135
+ * Handle dynamic calls to the container to set attributes.
136
+ *
137
+ * @param string $method
138
+ * @param array $parameters
139
+ * @return $this
140
+ */
141
+ public function __call($method, $parameters)
142
+ {
143
+ $this->attributes[$method] = count($parameters) > 0 ? $parameters[0] : true;
144
+
145
+ return $this;
146
+ }
147
+
148
+ /**
149
+ * Dynamically retrieve the value of an attribute.
150
+ *
151
+ * @param string $key
152
+ * @return mixed
153
+ */
154
+ public function __get($key)
155
+ {
156
+ return $this->get($key);
157
+ }
158
+
159
+ /**
160
+ * Dynamically set the value of an attribute.
161
+ *
162
+ * @param string $key
163
+ * @param mixed $value
164
+ * @return void
165
+ */
166
+ public function __set($key, $value)
167
+ {
168
+ $this->attributes[$key] = $value;
169
+ }
170
+
171
+ /**
172
+ * Dynamically check if an attribute is set.
173
+ *
174
+ * @param string $key
175
+ * @return bool
176
+ */
177
+ public function __isset($key)
178
+ {
179
+ return isset($this->attributes[$key]);
180
+ }
181
+
182
+ /**
183
+ * Dynamically unset an attribute.
184
+ *
185
+ * @param string $key
186
+ * @return void
187
+ */
188
+ public function __unset($key)
189
+ {
190
+ unset($this->attributes[$key]);
191
+ }
192
+ }
vendor/illuminate/support/HigherOrderCollectionProxy.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support;
4
+
5
+ /**
6
+ * @mixin \Illuminate\Support\Collection
7
+ */
8
+ class HigherOrderCollectionProxy
9
+ {
10
+ /**
11
+ * The collection being operated on.
12
+ *
13
+ * @var \Illuminate\Support\Collection
14
+ */
15
+ protected $collection;
16
+
17
+ /**
18
+ * The method being proxied.
19
+ *
20
+ * @var string
21
+ */
22
+ protected $method;
23
+
24
+ /**
25
+ * Create a new proxy instance.
26
+ *
27
+ * @param \Illuminate\Support\Collection $collection
28
+ * @param string $method
29
+ * @return void
30
+ */
31
+ public function __construct(Collection $collection, $method)
32
+ {
33
+ $this->method = $method;
34
+ $this->collection = $collection;
35
+ }
36
+
37
+ /**
38
+ * Proxy accessing an attribute onto the collection items.
39
+ *
40
+ * @param string $key
41
+ * @return mixed
42
+ */
43
+ public function __get($key)
44
+ {
45
+ return $this->collection->{$this->method}(function ($value) use ($key) {
46
+ return is_array($value) ? $value[$key] : $value->{$key};
47
+ });
48
+ }
49
+
50
+ /**
51
+ * Proxy a method call onto the collection items.
52
+ *
53
+ * @param string $method
54
+ * @param array $parameters
55
+ * @return mixed
56
+ */
57
+ public function __call($method, $parameters)
58
+ {
59
+ return $this->collection->{$this->method}(function ($value) use ($method, $parameters) {
60
+ return $value->{$method}(...$parameters);
61
+ });
62
+ }
63
+ }
vendor/illuminate/support/HigherOrderTapProxy.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support;
4
+
5
+ class HigherOrderTapProxy
6
+ {
7
+ /**
8
+ * The target being tapped.
9
+ *
10
+ * @var mixed
11
+ */
12
+ public $target;
13
+
14
+ /**
15
+ * Create a new tap proxy instance.
16
+ *
17
+ * @param mixed $target
18
+ * @return void
19
+ */
20
+ public function __construct($target)
21
+ {
22
+ $this->target = $target;
23
+ }
24
+
25
+ /**
26
+ * Dynamically pass method calls to the target.
27
+ *
28
+ * @param string $method
29
+ * @param array $parameters
30
+ * @return mixed
31
+ */
32
+ public function __call($method, $parameters)
33
+ {
34
+ $this->target->{$method}(...$parameters);
35
+
36
+ return $this->target;
37
+ }
38
+ }
vendor/illuminate/support/HtmlString.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support;
4
+
5
+ use Illuminate\Contracts\Support\Htmlable;
6
+
7
+ class HtmlString implements Htmlable
8
+ {
9
+ /**
10
+ * The HTML string.
11
+ *
12
+ * @var string
13
+ */
14
+ protected $html;
15
+
16
+ /**
17
+ * Create a new HTML string instance.
18
+ *
19
+ * @param string $html
20
+ * @return void
21
+ */
22
+ public function __construct($html)
23
+ {
24
+ $this->html = $html;
25
+ }
26
+
27
+ /**
28
+ * Get the HTML string.
29
+ *
30
+ * @return string
31
+ */
32
+ public function toHtml()
33
+ {
34
+ return $this->html;
35
+ }
36
+
37
+ /**
38
+ * Get the HTML string.
39
+ *
40
+ * @return string
41
+ */
42
+ public function __toString()
43
+ {
44
+ return $this->toHtml();
45
+ }
46
+ }
vendor/illuminate/support/Manager.php ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support;
4
+
5
+ use Closure;
6
+ use InvalidArgumentException;
7
+
8
+ abstract class Manager
9
+ {
10
+ /**
11
+ * The application instance.
12
+ *
13
+ * @var \Illuminate\Foundation\Application
14
+ */
15
+ protected $app;
16
+
17
+ /**
18
+ * The registered custom driver creators.
19
+ *
20
+ * @var array
21
+ */
22
+ protected $customCreators = [];
23
+
24
+ /**
25
+ * The array of created "drivers".
26
+ *
27
+ * @var array
28
+ */
29
+ protected $drivers = [];
30
+
31
+ /**
32
+ * Create a new manager instance.
33
+ *
34
+ * @param \Illuminate\Foundation\Application $app
35
+ * @return void
36
+ */
37
+ public function __construct($app)
38
+ {
39
+ $this->app = $app;
40
+ }
41
+
42
+ /**
43
+ * Get the default driver name.
44
+ *
45
+ * @return string
46
+ */
47
+ abstract public function getDefaultDriver();
48
+
49
+ /**
50
+ * Get a driver instance.
51
+ *
52
+ * @param string $driver
53
+ * @return mixed
54
+ */
55
+ public function driver($driver = null)
56
+ {
57
+ $driver = $driver ?: $this->getDefaultDriver();
58
+
59
+ // If the given driver has not been created before, we will create the instances
60
+ // here and cache it so we can return it next time very quickly. If there is
61
+ // already a driver created by this name, we'll just return that instance.
62
+ if (! isset($this->drivers[$driver])) {
63
+ $this->drivers[$driver] = $this->createDriver($driver);
64
+ }
65
+
66
+ return $this->drivers[$driver];
67
+ }
68
+
69
+ /**
70
+ * Create a new driver instance.
71
+ *
72
+ * @param string $driver
73
+ * @return mixed
74
+ *
75
+ * @throws \InvalidArgumentException
76
+ */
77
+ protected function createDriver($driver)
78
+ {
79
+ // We'll check to see if a creator method exists for the given driver. If not we
80
+ // will check for a custom driver creator, which allows developers to create
81
+ // drivers using their own customized driver creator Closure to create it.
82
+ if (isset($this->customCreators[$driver])) {
83
+ return $this->callCustomCreator($driver);
84
+ } else {
85
+ $method = 'create'.Str::studly($driver).'Driver';
86
+
87
+ if (method_exists($this, $method)) {
88
+ return $this->$method();
89
+ }
90
+ }
91
+ throw new InvalidArgumentException("Driver [$driver] not supported.");
92
+ }
93
+
94
+ /**
95
+ * Call a custom driver creator.
96
+ *
97
+ * @param string $driver
98
+ * @return mixed
99
+ */
100
+ protected function callCustomCreator($driver)
101
+ {
102
+ return $this->customCreators[$driver]($this->app);
103
+ }
104
+
105
+ /**
106
+ * Register a custom driver creator Closure.
107
+ *
108
+ * @param string $driver
109
+ * @param \Closure $callback
110
+ * @return $this
111
+ */
112
+ public function extend($driver, Closure $callback)
113
+ {
114
+ $this->customCreators[$driver] = $callback;
115
+
116
+ return $this;
117
+ }
118
+
119
+ /**
120
+ * Get all of the created "drivers".
121
+ *
122
+ * @return array
123
+ */
124
+ public function getDrivers()
125
+ {
126
+ return $this->drivers;
127
+ }
128
+
129
+ /**
130
+ * Dynamically call the default driver instance.
131
+ *
132
+ * @param string $method
133
+ * @param array $parameters
134
+ * @return mixed
135
+ */
136
+ public function __call($method, $parameters)
137
+ {
138
+ return $this->driver()->$method(...$parameters);
139
+ }
140
+ }
vendor/illuminate/support/MessageBag.php ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support;
4
+
5
+ use Countable;
6
+ use JsonSerializable;
7
+ use Illuminate\Contracts\Support\Jsonable;
8
+ use Illuminate\Contracts\Support\Arrayable;
9
+ use Illuminate\Contracts\Support\MessageProvider;
10
+ use Illuminate\Contracts\Support\MessageBag as MessageBagContract;
11
+
12
+ class MessageBag implements Arrayable, Countable, Jsonable, JsonSerializable, MessageBagContract, MessageProvider
13
+ {
14
+ /**
15
+ * All of the registered messages.
16
+ *
17
+ * @var array
18
+ */
19
+ protected $messages = [];
20
+
21
+ /**
22
+ * Default format for message output.
23
+ *
24
+ * @var string
25
+ */
26
+ protected $format = ':message';
27
+
28
+ /**
29
+ * Create a new message bag instance.
30
+ *
31
+ * @param array $messages
32
+ * @return void
33
+ */
34
+ public function __construct(array $messages = [])
35
+ {
36
+ foreach ($messages as $key => $value) {
37
+ $this->messages[$key] = (array) $value;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Get the keys present in the message bag.
43
+ *
44
+ * @return array
45
+ */
46
+ public function keys()
47
+ {
48
+ return array_keys($this->messages);
49
+ }
50
+
51
+ /**
52
+ * Add a message to the bag.
53
+ *
54
+ * @param string $key
55
+ * @param string $message
56
+ * @return $this
57
+ */
58
+ public function add($key, $message)
59
+ {
60
+ if ($this->isUnique($key, $message)) {
61
+ $this->messages[$key][] = $message;
62
+ }
63
+
64
+ return $this;
65
+ }
66
+
67
+ /**
68
+ * Determine if a key and message combination already exists.
69
+ *
70
+ * @param string $key
71
+ * @param string $message
72
+ * @return bool
73
+ */
74
+ protected function isUnique($key, $message)
75
+ {
76
+ $messages = (array) $this->messages;
77
+
78
+ return ! isset($messages[$key]) || ! in_array($message, $messages[$key]);
79
+ }
80
+
81
+ /**
82
+ * Merge a new array of messages into the bag.
83
+ *
84
+ * @param \Illuminate\Contracts\Support\MessageProvider|array $messages
85
+ * @return $this
86
+ */
87
+ public function merge($messages)
88
+ {
89
+ if ($messages instanceof MessageProvider) {
90
+ $messages = $messages->getMessageBag()->getMessages();
91
+ }
92
+
93
+ $this->messages = array_merge_recursive($this->messages, $messages);
94
+
95
+ return $this;
96
+ }
97
+
98
+ /**
99
+ * Determine if messages exist for all of the given keys.
100
+ *
101
+ * @param array|string $key
102
+ * @return bool
103
+ */
104
+ public function has($key)
105
+ {
106
+ if (is_null($key)) {
107
+ return $this->any();
108
+ }
109
+
110
+ $keys = is_array($key) ? $key : func_get_args();
111
+
112
+ foreach ($keys as $key) {
113
+ if ($this->first($key) === '') {
114
+ return false;
115
+ }
116
+ }
117
+
118
+ return true;
119
+ }
120
+
121
+ /**
122
+ * Determine if messages exist for any of the given keys.
123
+ *
124
+ * @param array|string $keys
125
+ * @return bool
126
+ */
127
+ public function hasAny($keys = [])
128
+ {
129
+ $keys = is_array($keys) ? $keys : func_get_args();
130
+
131
+ foreach ($keys as $key) {
132
+ if ($this->has($key)) {
133
+ return true;
134
+ }
135
+ }
136
+
137
+ return false;
138
+ }
139
+
140
+ /**
141
+ * Get the first message from the bag for a given key.
142
+ *
143
+ * @param string $key
144
+ * @param string $format
145
+ * @return string
146
+ */
147
+ public function first($key = null, $format = null)
148
+ {
149
+ $messages = is_null($key) ? $this->all($format) : $this->get($key, $format);
150
+
151
+ $firstMessage = Arr::first($messages, null, '');
152
+
153
+ return is_array($firstMessage) ? Arr::first($firstMessage) : $firstMessage;
154
+ }
155
+
156
+ /**
157
+ * Get all of the messages from the bag for a given key.
158
+ *
159
+ * @param string $key
160
+ * @param string $format
161
+ * @return array
162
+ */
163
+ public function get($key, $format = null)
164
+ {
165
+ // If the message exists in the container, we will transform it and return
166
+ // the message. Otherwise, we'll check if the key is implicit & collect
167
+ // all the messages that match a given key and output it as an array.
168
+ if (array_key_exists($key, $this->messages)) {
169
+ return $this->transform(
170
+ $this->messages[$key], $this->checkFormat($format), $key
171
+ );
172
+ }
173
+
174
+ if (Str::contains($key, '*')) {
175
+ return $this->getMessagesForWildcardKey($key, $format);
176
+ }
177
+
178
+ return [];
179
+ }
180
+
181
+ /**
182
+ * Get the messages for a wildcard key.
183
+ *
184
+ * @param string $key
185
+ * @param string|null $format
186
+ * @return array
187
+ */
188
+ protected function getMessagesForWildcardKey($key, $format)
189
+ {
190
+ return collect($this->messages)
191
+ ->filter(function ($messages, $messageKey) use ($key) {
192
+ return Str::is($key, $messageKey);
193
+ })
194
+ ->map(function ($messages, $messageKey) use ($format) {
195
+ return $this->transform(
196
+ $messages, $this->checkFormat($format), $messageKey
197
+ );
198
+ })->all();
199
+ }
200
+
201
+ /**
202
+ * Get all of the messages for every key in the bag.
203
+ *
204
+ * @param string $format
205
+ * @return array
206
+ */
207
+ public function all($format = null)
208
+ {
209
+ $format = $this->checkFormat($format);
210
+
211
+ $all = [];
212
+
213
+ foreach ($this->messages as $key => $messages) {
214
+ $all = array_merge($all, $this->transform($messages, $format, $key));
215
+ }
216
+
217
+ return $all;
218
+ }
219
+
220
+ /**
221
+ * Get all of the unique messages for every key in the bag.
222
+ *
223
+ * @param string $format
224
+ * @return array
225
+ */
226
+ public function unique($format = null)
227
+ {
228
+ return array_unique($this->all($format));
229
+ }
230
+
231
+ /**
232
+ * Format an array of messages.
233
+ *
234
+ * @param array $messages
235
+ * @param string $format
236
+ * @param string $messageKey
237
+ * @return array
238
+ */
239
+ protected function transform($messages, $format, $messageKey)
240
+ {
241
+ return collect((array) $messages)
242
+ ->map(function ($message) use ($format, $messageKey) {
243
+ // We will simply spin through the given messages and transform each one
244
+ // replacing the :message place holder with the real message allowing
245
+ // the messages to be easily formatted to each developer's desires.
246
+ return str_replace([':message', ':key'], [$message, $messageKey], $format);
247
+ })->all();
248
+ }
249
+
250
+ /**
251
+ * Get the appropriate format based on the given format.
252
+ *
253
+ * @param string $format
254
+ * @return string
255
+ */
256
+ protected function checkFormat($format)
257
+ {
258
+ return $format ?: $this->format;
259
+ }
260
+
261
+ /**
262
+ * Get the raw messages in the container.
263
+ *
264
+ * @return array
265
+ */
266
+ public function messages()
267
+ {
268
+ return $this->messages;
269
+ }
270
+
271
+ /**
272
+ * Get the raw messages in the container.
273
+ *
274
+ * @return array
275
+ */
276
+ public function getMessages()
277
+ {
278
+ return $this->messages();
279
+ }
280
+
281
+ /**
282
+ * Get the messages for the instance.
283
+ *
284
+ * @return \Illuminate\Support\MessageBag
285
+ */
286
+ public function getMessageBag()
287
+ {
288
+ return $this;
289
+ }
290
+
291
+ /**
292
+ * Get the default message format.
293
+ *
294
+ * @return string
295
+ */
296
+ public function getFormat()
297
+ {
298
+ return $this->format;
299
+ }
300
+
301
+ /**
302
+ * Set the default message format.
303
+ *
304
+ * @param string $format
305
+ * @return \Illuminate\Support\MessageBag
306
+ */
307
+ public function setFormat($format = ':message')
308
+ {
309
+ $this->format = $format;
310
+
311
+ return $this;
312
+ }
313
+
314
+ /**
315
+ * Determine if the message bag has any messages.
316
+ *
317
+ * @return bool
318
+ */
319
+ public function isEmpty()
320
+ {
321
+ return ! $this->any();
322
+ }
323
+
324
+ /**
325
+ * Determine if the message bag has any messages.
326
+ *
327
+ * @return bool
328
+ */
329
+ public function any()
330
+ {
331
+ return $this->count() > 0;
332
+ }
333
+
334
+ /**
335
+ * Get the number of messages in the container.
336
+ *
337
+ * @return int
338
+ */
339
+ public function count()
340
+ {
341
+ return count($this->messages, COUNT_RECURSIVE) - count($this->messages);
342
+ }
343
+
344
+ /**
345
+ * Get the instance as an array.
346
+ *
347
+ * @return array
348
+ */
349
+ public function toArray()
350
+ {
351
+ return $this->getMessages();
352
+ }
353
+
354
+ /**
355
+ * Convert the object into something JSON serializable.
356
+ *
357
+ * @return array
358
+ */
359
+ public function jsonSerialize()
360
+ {
361
+ return $this->toArray();
362
+ }
363
+
364
+ /**
365
+ * Convert the object to its JSON representation.
366
+ *
367
+ * @param int $options
368
+ * @return string
369
+ */
370
+ public function toJson($options = 0)
371
+ {
372
+ return json_encode($this->jsonSerialize(), $options);
373
+ }
374
+
375
+ /**
376
+ * Convert the message bag to its string representation.
377
+ *
378
+ * @return string
379
+ */
380
+ public function __toString()
381
+ {
382
+ return $this->toJson();
383
+ }
384
+ }
vendor/illuminate/support/NamespacedItemResolver.php ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support;
4
+
5
+ class NamespacedItemResolver
6
+ {
7
+ /**
8
+ * A cache of the parsed items.
9
+ *
10
+ * @var array
11
+ */
12
+ protected $parsed = [];
13
+
14
+ /**
15
+ * Parse a key into namespace, group, and item.
16
+ *
17
+ * @param string $key
18
+ * @return array
19
+ */
20
+ public function parseKey($key)
21
+ {
22
+ // If we've already parsed the given key, we'll return the cached version we
23
+ // already have, as this will save us some processing. We cache off every
24
+ // key we parse so we can quickly return it on all subsequent requests.
25
+ if (isset($this->parsed[$key])) {
26
+ return $this->parsed[$key];
27
+ }
28
+
29
+ // If the key does not contain a double colon, it means the key is not in a
30
+ // namespace, and is just a regular configuration item. Namespaces are a
31
+ // tool for organizing configuration items for things such as modules.
32
+ if (strpos($key, '::') === false) {
33
+ $segments = explode('.', $key);
34
+
35
+ $parsed = $this->parseBasicSegments($segments);
36
+ } else {
37
+ $parsed = $this->parseNamespacedSegments($key);
38
+ }
39
+
40
+ // Once we have the parsed array of this key's elements, such as its groups
41
+ // and namespace, we will cache each array inside a simple list that has
42
+ // the key and the parsed array for quick look-ups for later requests.
43
+ return $this->parsed[$key] = $parsed;
44
+ }
45
+
46
+ /**
47
+ * Parse an array of basic segments.
48
+ *
49
+ * @param array $segments
50
+ * @return array
51
+ */
52
+ protected function parseBasicSegments(array $segments)
53
+ {
54
+ // The first segment in a basic array will always be the group, so we can go
55
+ // ahead and grab that segment. If there is only one total segment we are
56
+ // just pulling an entire group out of the array and not a single item.
57
+ $group = $segments[0];
58
+
59
+ if (count($segments) == 1) {
60
+ return [null, $group, null];
61
+ }
62
+
63
+ // If there is more than one segment in this group, it means we are pulling
64
+ // a specific item out of a group and will need to return this item name
65
+ // as well as the group so we know which item to pull from the arrays.
66
+ else {
67
+ $item = implode('.', array_slice($segments, 1));
68
+
69
+ return [null, $group, $item];
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Parse an array of namespaced segments.
75
+ *
76
+ * @param string $key
77
+ * @return array
78
+ */
79
+ protected function parseNamespacedSegments($key)
80
+ {
81
+ list($namespace, $item) = explode('::', $key);
82
+
83
+ // First we'll just explode the first segment to get the namespace and group
84
+ // since the item should be in the remaining segments. Once we have these
85
+ // two pieces of data we can proceed with parsing out the item's value.
86
+ $itemSegments = explode('.', $item);
87
+
88
+ $groupAndItem = array_slice(
89
+ $this->parseBasicSegments($itemSegments), 1
90
+ );
91
+
92
+ return array_merge([$namespace], $groupAndItem);
93
+ }
94
+
95
+ /**
96
+ * Set the parsed value of a key.
97
+ *
98
+ * @param string $key
99
+ * @param array $parsed
100
+ * @return void
101
+ */
102
+ public function setParsedKey($key, $parsed)
103
+ {
104
+ $this->parsed[$key] = $parsed;
105
+ }
106
+ }
vendor/illuminate/support/Pluralizer.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support;
4
+
5
+ use Doctrine\Common\Inflector\Inflector;
6
+
7
+ class Pluralizer
8
+ {
9
+ /**
10
+ * Uncountable word forms.
11
+ *
12
+ * @var array
13
+ */
14
+ public static $uncountable = [
15
+ 'audio',
16
+ 'bison',
17
+ 'chassis',
18
+ 'compensation',
19
+ 'coreopsis',
20
+ 'data',
21
+ 'deer',
22
+ 'education',
23
+ 'emoji',
24
+ 'equipment',
25
+ 'evidence',
26
+ 'feedback',
27
+ 'fish',
28
+ 'furniture',
29
+ 'gold',
30
+ 'information',
31
+ 'jedi',
32
+ 'knowledge',
33
+ 'love',
34
+ 'metadata',
35
+ 'money',
36
+ 'moose',
37
+ 'news',
38
+ 'nutrition',
39
+ 'offspring',
40
+ 'plankton',
41
+ 'pokemon',
42
+ 'police',
43
+ 'rain',
44
+ 'rice',
45
+ 'series',
46
+ 'sheep',
47
+ 'species',
48
+ 'swine',
49
+ 'traffic',
50
+ 'wheat',
51
+ ];
52
+
53
+ /**
54
+ * Get the plural form of an English word.
55
+ *
56
+ * @param string $value
57
+ * @param int $count
58
+ * @return string
59
+ */
60
+ public static function plural($value, $count = 2)
61
+ {
62
+ if ((int) $count === 1 || static::uncountable($value)) {
63
+ return $value;
64
+ }
65
+
66
+ $plural = Inflector::pluralize($value);
67
+
68
+ return static::matchCase($plural, $value);
69
+ }
70
+
71
+ /**
72
+ * Get the singular form of an English word.
73
+ *
74
+ * @param string $value
75
+ * @return string
76
+ */
77
+ public static function singular($value)
78
+ {
79
+ $singular = Inflector::singularize($value);
80
+
81
+ return static::matchCase($singular, $value);
82
+ }
83
+
84
+ /**
85
+ * Determine if the given value is uncountable.
86
+ *
87
+ * @param string $value
88
+ * @return bool
89
+ */
90
+ protected static function uncountable($value)
91
+ {
92
+ return in_array(strtolower($value), static::$uncountable);
93
+ }
94
+
95
+ /**
96
+ * Attempt to match the case on two strings.
97
+ *
98
+ * @param string $value
99
+ * @param string $comparison
100
+ * @return string
101
+ */
102
+ protected static function matchCase($value, $comparison)
103
+ {
104
+ $functions = ['mb_strtolower', 'mb_strtoupper', 'ucfirst', 'ucwords'];
105
+
106
+ foreach ($functions as $function) {
107
+ if (call_user_func($function, $comparison) === $comparison) {
108
+ return call_user_func($function, $value);
109
+ }
110
+ }
111
+
112
+ return $value;
113
+ }
114
+ }
vendor/illuminate/support/ServiceProvider.php ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support;
4
+
5
+ use Illuminate\Console\Application as Artisan;
6
+
7
+ abstract class ServiceProvider
8
+ {
9
+ /**
10
+ * The application instance.
11
+ *
12
+ * @var \Illuminate\Contracts\Foundation\Application
13
+ */
14
+ protected $app;
15
+
16
+ /**
17
+ * Indicates if loading of the provider is deferred.
18
+ *
19
+ * @var bool
20
+ */
21
+ protected $defer = false;
22
+
23
+ /**
24
+ * The paths that should be published.
25
+ *
26
+ * @var array
27
+ */
28
+ protected static $publishes = [];
29
+
30
+ /**
31
+ * The paths that should be published by group.
32
+ *
33
+ * @var array
34
+ */
35
+ protected static $publishGroups = [];
36
+
37
+ /**
38
+ * Create a new service provider instance.
39
+ *
40
+ * @param \Illuminate\Contracts\Foundation\Application $app
41
+ * @return void
42
+ */
43
+ public function __construct($app)
44
+ {
45
+ $this->app = $app;
46
+ }
47
+
48
+ /**
49
+ * Merge the given configuration with the existing configuration.
50
+ *
51
+ * @param string $path
52
+ * @param string $key
53
+ * @return void
54
+ */
55
+ protected function mergeConfigFrom($path, $key)
56
+ {
57
+ $config = $this->app['config']->get($key, []);
58
+
59
+ $this->app['config']->set($key, array_merge(require $path, $config));
60
+ }
61
+
62
+ /**
63
+ * Load the given routes file if routes are not already cached.
64
+ *
65
+ * @param string $path
66
+ * @return void
67
+ */
68
+ protected function loadRoutesFrom($path)
69
+ {
70
+ if (! $this->app->routesAreCached()) {
71
+ require $path;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Register a view file namespace.
77
+ *
78
+ * @param string $path
79
+ * @param string $namespace
80
+ * @return void
81
+ */
82
+ protected function loadViewsFrom($path, $namespace)
83
+ {
84
+ if (is_dir($appPath = $this->app->resourcePath().'/views/vendor/'.$namespace)) {
85
+ $this->app['view']->addNamespace($namespace, $appPath);
86
+ }
87
+
88
+ $this->app['view']->addNamespace($namespace, $path);
89
+ }
90
+
91
+ /**
92
+ * Register a translation file namespace.
93
+ *
94
+ * @param string $path
95
+ * @param string $namespace
96
+ * @return void
97
+ */
98
+ protected function loadTranslationsFrom($path, $namespace)
99
+ {
100
+ $this->app['translator']->addNamespace($namespace, $path);
101
+ }
102
+
103
+ /**
104
+ * Register a database migration path.
105
+ *
106
+ * @param array|string $paths
107
+ * @return void
108
+ */
109
+ protected function loadMigrationsFrom($paths)
110
+ {
111
+ $this->app->afterResolving('migrator', function ($migrator) use ($paths) {
112
+ foreach ((array) $paths as $path) {
113
+ $migrator->path($path);
114
+ }
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Register paths to be published by the publish command.
120
+ *
121
+ * @param array $paths
122
+ * @param string $group
123
+ * @return void
124
+ */
125
+ protected function publishes(array $paths, $group = null)
126
+ {
127
+ $this->ensurePublishArrayInitialized($class = static::class);
128
+
129
+ static::$publishes[$class] = array_merge(static::$publishes[$class], $paths);
130
+
131
+ if ($group) {
132
+ $this->addPublishGroup($group, $paths);
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Ensure the publish array for the service provider is initialized.
138
+ *
139
+ * @param string $class
140
+ * @return void
141
+ */
142
+ protected function ensurePublishArrayInitialized($class)
143
+ {
144
+ if (! array_key_exists($class, static::$publishes)) {
145
+ static::$publishes[$class] = [];
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Add a publish group / tag to the service provider.
151
+ *
152
+ * @param string $group
153
+ * @param array $paths
154
+ * @return void
155
+ */
156
+ protected function addPublishGroup($group, $paths)
157
+ {
158
+ if (! array_key_exists($group, static::$publishGroups)) {
159
+ static::$publishGroups[$group] = [];
160
+ }
161
+
162
+ static::$publishGroups[$group] = array_merge(
163
+ static::$publishGroups[$group], $paths
164
+ );
165
+ }
166
+
167
+ /**
168
+ * Get the paths to publish.
169
+ *
170
+ * @param string $provider
171
+ * @param string $group
172
+ * @return array
173
+ */
174
+ public static function pathsToPublish($provider = null, $group = null)
175
+ {
176
+ if (! is_null($paths = static::pathsForProviderOrGroup($provider, $group))) {
177
+ return $paths;
178
+ }
179
+
180
+ return collect(static::$publishes)->reduce(function ($paths, $p) {
181
+ return array_merge($paths, $p);
182
+ }, []);
183
+ }
184
+
185
+ /**
186
+ * Get the paths for the provider or group (or both).
187
+ *
188
+ * @param string|null $provider
189
+ * @param string|null $group
190
+ * @return array
191
+ */
192
+ protected static function pathsForProviderOrGroup($provider, $group)
193
+ {
194
+ if ($provider && $group) {
195
+ return static::pathsForProviderAndGroup($provider, $group);
196
+ } elseif ($group && array_key_exists($group, static::$publishGroups)) {
197
+ return static::$publishGroups[$group];
198
+ } elseif ($provider && array_key_exists($provider, static::$publishes)) {
199
+ return static::$publishes[$provider];
200
+ } elseif ($group || $provider) {
201
+ return [];
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Get the paths for the provider and group.
207
+ *
208
+ * @param string $provider
209
+ * @param string $group
210
+ * @return array
211
+ */
212
+ protected static function pathsForProviderAndGroup($provider, $group)
213
+ {
214
+ if (! empty(static::$publishes[$provider]) && ! empty(static::$publishGroups[$group])) {
215
+ return array_intersect_key(static::$publishes[$provider], static::$publishGroups[$group]);
216
+ }
217
+
218
+ return [];
219
+ }
220
+
221
+ /**
222
+ * Register the package's custom Artisan commands.
223
+ *
224
+ * @param array|mixed $commands
225
+ * @return void
226
+ */
227
+ public function commands($commands)
228
+ {
229
+ $commands = is_array($commands) ? $commands : func_get_args();
230
+
231
+ Artisan::starting(function ($artisan) use ($commands) {
232
+ $artisan->resolveCommands($commands);
233
+ });
234
+ }
235
+
236
+ /**
237
+ * Get the services provided by the provider.
238
+ *
239
+ * @return array
240
+ */
241
+ public function provides()
242
+ {
243
+ return [];
244
+ }
245
+
246
+ /**
247
+ * Get the events that trigger this service provider to register.
248
+ *
249
+ * @return array
250
+ */
251
+ public function when()
252
+ {
253
+ return [];
254
+ }
255
+
256
+ /**
257
+ * Determine if the provider is deferred.
258
+ *
259
+ * @return bool
260
+ */
261
+ public function isDeferred()
262
+ {
263
+ return $this->defer;
264
+ }
265
+
266
+ /**
267
+ * Get a list of files that should be compiled for the package.
268
+ *
269
+ * @deprecated
270
+ *
271
+ * @return array
272
+ */
273
+ public static function compiles()
274
+ {
275
+ return [];
276
+ }
277
+ }
vendor/illuminate/support/Str.php ADDED
@@ -0,0 +1,646 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support;
4
+
5
+ use Illuminate\Support\Traits\Macroable;
6
+
7
+ class Str
8
+ {
9
+ use Macroable;
10
+
11
+ /**
12
+ * The cache of snake-cased words.
13
+ *
14
+ * @var array
15
+ */
16
+ protected static $snakeCache = [];
17
+
18
+ /**
19
+ * The cache of camel-cased words.
20
+ *
21
+ * @var array
22
+ */
23
+ protected static $camelCache = [];
24
+
25
+ /**
26
+ * The cache of studly-cased words.
27
+ *
28
+ * @var array
29
+ */
30
+ protected static $studlyCache = [];
31
+
32
+ /**
33
+ * Return the remainder of a string after a given value.
34
+ *
35
+ * @param string $subject
36
+ * @param string $search
37
+ * @return string
38
+ */
39
+ public static function after($subject, $search)
40
+ {
41
+ if ($search == '') {
42
+ return $subject;
43
+ }
44
+
45
+ $pos = strpos($subject, $search);
46
+
47
+ if ($pos === false) {
48
+ return $subject;
49
+ }
50
+
51
+ return substr($subject, $pos + strlen($search));
52
+ }
53
+
54
+ /**
55
+ * Transliterate a UTF-8 value to ASCII.
56
+ *
57
+ * @param string $value
58
+ * @return string
59
+ */
60
+ public static function ascii($value)
61
+ {
62
+ foreach (static::charsArray() as $key => $val) {
63
+ $value = str_replace($val, $key, $value);
64
+ }
65
+
66
+ return preg_replace('/[^\x20-\x7E]/u', '', $value);
67
+ }
68
+
69
+ /**
70
+ * Convert a value to camel case.
71
+ *
72
+ * @param string $value
73
+ * @return string
74
+ */
75
+ public static function camel($value)
76
+ {
77
+ if (isset(static::$camelCache[$value])) {
78
+ return static::$camelCache[$value];
79
+ }
80
+
81
+ return static::$camelCache[$value] = lcfirst(static::studly($value));
82
+ }
83
+
84
+ /**
85
+ * Determine if a given string contains a given substring.
86
+ *
87
+ * @param string $haystack
88
+ * @param string|array $needles
89
+ * @return bool
90
+ */
91
+ public static function contains($haystack, $needles)
92
+ {
93
+ foreach ((array) $needles as $needle) {
94
+ if ($needle != '' && mb_strpos($haystack, $needle) !== false) {
95
+ return true;
96
+ }
97
+ }
98
+
99
+ return false;
100
+ }
101
+
102
+ /**
103
+ * Determine if a given string ends with a given substring.
104
+ *
105
+ * @param string $haystack
106
+ * @param string|array $needles
107
+ * @return bool
108
+ */
109
+ public static function endsWith($haystack, $needles)
110
+ {
111
+ foreach ((array) $needles as $needle) {
112
+ if (substr($haystack, -strlen($needle)) === (string) $needle) {
113
+ return true;
114
+ }
115
+ }
116
+
117
+ return false;
118
+ }
119
+
120
+ /**
121
+ * Cap a string with a single instance of a given value.
122
+ *
123
+ * @param string $value
124
+ * @param string $cap
125
+ * @return string
126
+ */
127
+ public static function finish($value, $cap)
128
+ {
129
+ $quoted = preg_quote($cap, '/');
130
+
131
+ return preg_replace('/(?:'.$quoted.')+$/u', '', $value).$cap;
132
+ }
133
+
134
+ /**
135
+ * Determine if a given string matches a given pattern.
136
+ *
137
+ * @param string $pattern
138
+ * @param string $value
139
+ * @return bool
140
+ */
141
+ public static function is($pattern, $value)
142
+ {
143
+ if ($pattern == $value) {
144
+ return true;
145
+ }
146
+
147
+ $pattern = preg_quote($pattern, '#');
148
+
149
+ // Asterisks are translated into zero-or-more regular expression wildcards
150
+ // to make it convenient to check if the strings starts with the given
151
+ // pattern such as "library/*", making any string check convenient.
152
+ $pattern = str_replace('\*', '.*', $pattern);
153
+
154
+ return (bool) preg_match('#^'.$pattern.'\z#u', $value);
155
+ }
156
+
157
+ /**
158
+ * Convert a string to kebab case.
159
+ *
160
+ * @param string $value
161
+ * @return string
162
+ */
163
+ public static function kebab($value)
164
+ {
165
+ return static::snake($value, '-');
166
+ }
167
+
168
+ /**
169
+ * Return the length of the given string.
170
+ *
171
+ * @param string $value
172
+ * @param string $encoding
173
+ * @return int
174
+ */
175
+ public static function length($value, $encoding = null)
176
+ {
177
+ if ($encoding) {
178
+ return mb_strlen($value, $encoding);
179
+ }
180
+
181
+ return mb_strlen($value);
182
+ }
183
+
184
+ /**
185
+ * Limit the number of characters in a string.
186
+ *
187
+ * @param string $value
188
+ * @param int $limit
189
+ * @param string $end
190
+ * @return string
191
+ */
192
+ public static function limit($value, $limit = 100, $end = '...')
193
+ {
194
+ if (mb_strwidth($value, 'UTF-8') <= $limit) {
195
+ return $value;
196
+ }
197
+
198
+ return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')).$end;
199
+ }
200
+
201
+ /**
202
+ * Convert the given string to lower-case.
203
+ *
204
+ * @param string $value
205
+ * @return string
206
+ */
207
+ public static function lower($value)
208
+ {
209
+ return mb_strtolower($value, 'UTF-8');
210
+ }
211
+
212
+ /**
213
+ * Limit the number of words in a string.
214
+ *
215
+ * @param string $value
216
+ * @param int $words
217
+ * @param string $end
218
+ * @return string
219
+ */
220
+ public static function words($value, $words = 100, $end = '...')
221
+ {
222
+ preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/u', $value, $matches);
223
+
224
+ if (! isset($matches[0]) || static::length($value) === static::length($matches[0])) {
225
+ return $value;
226
+ }
227
+
228
+ return rtrim($matches[0]).$end;
229
+ }
230
+
231
+ /**
232
+ * Parse a Class@method style callback into class and method.
233
+ *
234
+ * @param string $callback
235
+ * @param string|null $default
236
+ * @return array
237
+ */
238
+ public static function parseCallback($callback, $default = null)
239
+ {
240
+ return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
241
+ }
242
+
243
+ /**
244
+ * Get the plural form of an English word.
245
+ *
246
+ * @param string $value
247
+ * @param int $count
248
+ * @return string
249
+ */
250
+ public static function plural($value, $count = 2)
251
+ {
252
+ return Pluralizer::plural($value, $count);
253
+ }
254
+
255
+ /**
256
+ * Generate a more truly "random" alpha-numeric string.
257
+ *
258
+ * @param int $length
259
+ * @return string
260
+ */
261
+ public static function random($length = 16)
262
+ {
263
+ $string = '';
264
+
265
+ while (($len = strlen($string)) < $length) {
266
+ $size = $length - $len;
267
+
268
+ $bytes = random_bytes($size);
269
+
270
+ $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
271
+ }
272
+
273
+ return $string;
274
+ }
275
+
276
+ /**
277
+ * Generate a "random" alpha-numeric string.
278
+ *
279
+ * Should not be considered sufficient for cryptography, etc.
280
+ *
281
+ * @deprecated since version 5.3. Use the "random" method directly.
282
+ *
283
+ * @param int $length
284
+ * @return string
285
+ */
286
+ public static function quickRandom($length = 16)
287
+ {
288
+ if (PHP_MAJOR_VERSION > 5) {
289
+ return static::random($length);
290
+ }
291
+
292
+ $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
293
+
294
+ return substr(str_shuffle(str_repeat($pool, $length)), 0, $length);
295
+ }
296
+
297
+ /**
298
+ * Replace a given value in the string sequentially with an array.
299
+ *
300
+ * @param string $search
301
+ * @param array $replace
302
+ * @param string $subject
303
+ * @return string
304
+ */
305
+ public static function replaceArray($search, array $replace, $subject)
306
+ {
307
+ foreach ($replace as $value) {
308
+ $subject = static::replaceFirst($search, $value, $subject);
309
+ }
310
+
311
+ return $subject;
312
+ }
313
+
314
+ /**
315
+ * Replace the first occurrence of a given value in the string.
316
+ *
317
+ * @param string $search
318
+ * @param string $replace
319
+ * @param string $subject
320
+ * @return string
321
+ */
322
+ public static function replaceFirst($search, $replace, $subject)
323
+ {
324
+ if ($search == '') {
325
+ return $subject;
326
+ }
327
+
328
+ $position = strpos($subject, $search);
329
+
330
+ if ($position !== false) {
331
+ return substr_replace($subject, $replace, $position, strlen($search));
332
+ }
333
+
334
+ return $subject;
335
+ }
336
+
337
+ /**
338
+ * Replace the last occurrence of a given value in the string.
339
+ *
340
+ * @param string $search
341
+ * @param string $replace
342
+ * @param string $subject
343
+ * @return string
344
+ */
345
+ public static function replaceLast($search, $replace, $subject)
346
+ {
347
+ $position = strrpos($subject, $search);
348
+
349
+ if ($position !== false) {
350
+ return substr_replace($subject, $replace, $position, strlen($search));
351
+ }
352
+
353
+ return $subject;
354
+ }
355
+
356
+ /**
357
+ * Begin a string with a single instance of a given value.
358
+ *
359
+ * @param string $value
360
+ * @param string $prefix
361
+ * @return string
362
+ */
363
+ public static function start($value, $prefix)
364
+ {
365
+ $quoted = preg_quote($prefix, '/');
366
+
367
+ return $prefix.preg_replace('/^(?:'.$quoted.')+/u', '', $value);
368
+ }
369
+
370
+ /**
371
+ * Convert the given string to upper-case.
372
+ *
373
+ * @param string $value
374
+ * @return string
375
+ */
376
+ public static function upper($value)
377
+ {
378
+ return mb_strtoupper($value, 'UTF-8');
379
+ }
380
+
381
+ /**
382
+ * Convert the given string to title case.
383
+ *
384
+ * @param string $value
385
+ * @return string
386
+ */
387
+ public static function title($value)
388
+ {
389
+ return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
390
+ }
391
+
392
+ /**
393
+ * Get the singular form of an English word.
394
+ *
395
+ * @param string $value
396
+ * @return string
397
+ */
398
+ public static function singular($value)
399
+ {
400
+ return Pluralizer::singular($value);
401
+ }
402
+
403
+ /**
404
+ * Generate a URL friendly "slug" from a given string.
405
+ *
406
+ * @param string $title
407
+ * @param string $separator
408
+ * @return string
409
+ */
410
+ public static function slug($title, $separator = '-')
411
+ {
412
+ $title = static::ascii($title);
413
+
414
+ // Convert all dashes/underscores into separator
415
+ $flip = $separator == '-' ? '_' : '-';
416
+
417
+ $title = preg_replace('!['.preg_quote($flip).']+!u', $separator, $title);
418
+
419
+ // Remove all characters that are not the separator, letters, numbers, or whitespace.
420
+ $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', mb_strtolower($title));
421
+
422
+ // Replace all separator characters and whitespace by a single separator
423
+ $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title);
424
+
425
+ return trim($title, $separator);
426
+ }
427
+
428
+ /**
429
+ * Convert a string to snake case.
430
+ *
431
+ * @param string $value
432
+ * @param string $delimiter
433
+ * @return string
434
+ */
435
+ public static function snake($value, $delimiter = '_')
436
+ {
437
+ $key = $value;
438
+
439
+ if (isset(static::$snakeCache[$key][$delimiter])) {
440
+ return static::$snakeCache[$key][$delimiter];
441
+ }
442
+
443
+ if (! ctype_lower($value)) {
444
+ $value = preg_replace('/\s+/u', '', $value);
445
+
446
+ $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));
447
+ }
448
+
449
+ return static::$snakeCache[$key][$delimiter] = $value;
450
+ }
451
+
452
+ /**
453
+ * Determine if a given string starts with a given substring.
454
+ *
455
+ * @param string $haystack
456
+ * @param string|array $needles
457
+ * @return bool
458
+ */
459
+ public static function startsWith($haystack, $needles)
460
+ {
461
+ foreach ((array) $needles as $needle) {
462
+ if ($needle != '' && substr($haystack, 0, strlen($needle)) === (string) $needle) {
463
+ return true;
464
+ }
465
+ }
466
+
467
+ return false;
468
+ }
469
+
470
+ /**
471
+ * Convert a value to studly caps case.
472
+ *
473
+ * @param string $value
474
+ * @return string
475
+ */
476
+ public static function studly($value)
477
+ {
478
+ $key = $value;
479
+
480
+ if (isset(static::$studlyCache[$key])) {
481
+ return static::$studlyCache[$key];
482
+ }
483
+
484
+ $value = ucwords(str_replace(['-', '_'], ' ', $value));
485
+
486
+ return static::$studlyCache[$key] = str_replace(' ', '', $value);
487
+ }
488
+
489
+ /**
490
+ * Returns the portion of string specified by the start and length parameters.
491
+ *
492
+ * @param string $string
493
+ * @param int $start
494
+ * @param int|null $length
495
+ * @return string
496
+ */
497
+ public static function substr($string, $start, $length = null)
498
+ {
499
+ return mb_substr($string, $start, $length, 'UTF-8');
500
+ }
501
+
502
+ /**
503
+ * Make a string's first character uppercase.
504
+ *
505
+ * @param string $string
506
+ * @return string
507
+ */
508
+ public static function ucfirst($string)
509
+ {
510
+ return static::upper(static::substr($string, 0, 1)).static::substr($string, 1);
511
+ }
512
+
513
+ /**
514
+ * Returns the replacements for the ascii method.
515
+ *
516
+ * Note: Adapted from Stringy\Stringy.
517
+ *
518
+ * @see https://github.com/danielstjules/Stringy/blob/2.3.1/LICENSE.txt
519
+ *
520
+ * @return array
521
+ */
522
+ protected static function charsArray()
523
+ {
524
+ static $charsArray;
525
+
526
+ if (isset($charsArray)) {
527
+ return $charsArray;
528
+ }
529
+
530
+ return $charsArray = [
531
+ '0' => ['°', '₀', '۰'],
532
+ '1' => ['¹', '₁', '۱'],
533
+ '2' => ['²', '₂', '۲'],
534
+ '3' => ['³', '₃', '۳'],
535
+ '4' => ['⁴', '₄', '۴', '٤'],
536
+ '5' => ['⁵', '₅', '۵', '٥'],
537
+ '6' => ['⁶', '₆', '۶', '٦'],
538
+ '7' => ['⁷', '₇', '۷'],
539
+ '8' => ['⁸', '₈', '۸'],
540
+ '9' => ['⁹', '₉', '۹'],
541
+ 'a' => ['à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ', 'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ā', 'ą', 'å', 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ', 'ἇ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ', 'ά', 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ', 'အ', 'ာ', 'ါ', 'ǻ', 'ǎ', 'ª', 'ა', 'अ', 'ا'],
542
+ 'b' => ['б', 'β', 'Ъ', 'Ь', 'ب', 'ဗ', 'ბ'],
543
+ 'c' => ['ç', 'ć', 'č', 'ĉ', 'ċ'],
544
+ 'd' => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ'],
545
+ 'e' => ['é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ', 'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ', 'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э', 'є', 'ə', 'ဧ', 'ေ', 'ဲ', 'ე', 'ए', 'إ', 'ئ'],
546
+ 'f' => ['ф', 'φ', 'ف', 'ƒ', 'ფ'],
547
+ 'g' => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ဂ', 'გ', 'گ'],
548
+ 'h' => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ'],
549
+ 'i' => ['í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į', 'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ', 'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'і', 'ї', 'и', 'ဣ', 'ိ', 'ီ', 'ည်', 'ǐ', 'ი', 'इ'],
550
+ 'j' => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج'],
551
+ 'k' => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ', 'ک'],
552
+ 'l' => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ'],
553
+ 'm' => ['м', 'μ', 'م', 'မ', 'მ'],
554
+ 'n' => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', 'ნ'],
555
+ 'o' => ['ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ', 'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő', 'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό', 'о', 'و', 'θ', 'ို', 'ǒ', 'ǿ', 'º', 'ო', 'ओ'],
556
+ 'p' => ['п', 'π', 'ပ', 'პ', 'پ'],
557
+ 'q' => ['ყ'],
558
+ 'r' => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ'],
559
+ 's' => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', 'ſ', 'ს'],
560
+ 't' => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', 'თ', 'ტ'],
561
+ 'u' => ['ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ', 'ự', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у', 'ဉ', 'ု', 'ူ', 'ǔ', 'ǖ', 'ǘ', 'ǚ', 'ǜ', 'უ', 'उ'],
562
+ 'v' => ['в', 'ვ', 'ϐ'],
563
+ 'w' => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ'],
564
+ 'x' => ['χ', 'ξ'],
565
+ 'y' => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ'],
566
+ 'z' => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ'],
567
+ 'aa' => ['ع', 'आ', 'آ'],
568
+ 'ae' => ['ä', 'æ', 'ǽ'],
569
+ 'ai' => ['ऐ'],
570
+ 'at' => ['@'],
571
+ 'ch' => ['ч', 'ჩ', 'ჭ', 'چ'],
572
+ 'dj' => ['ђ', 'đ'],
573
+ 'dz' => ['џ', 'ძ'],
574
+ 'ei' => ['ऍ'],
575
+ 'gh' => ['غ', 'ღ'],
576
+ 'ii' => ['ई'],
577
+ 'ij' => ['ij'],
578
+ 'kh' => ['х', 'خ', 'ხ'],
579
+ 'lj' => ['љ'],
580
+ 'nj' => ['њ'],
581
+ 'oe' => ['ö', 'œ', 'ؤ'],
582
+ 'oi' => ['ऑ'],
583
+ 'oii' => ['ऒ'],
584
+ 'ps' => ['ψ'],
585
+ 'sh' => ['ш', 'შ', 'ش'],
586
+ 'shch' => ['щ'],
587
+ 'ss' => ['ß'],
588
+ 'sx' => ['ŝ'],
589
+ 'th' => ['þ', 'ϑ', 'ث', 'ذ', 'ظ'],
590
+ 'ts' => ['ц', 'ც', 'წ'],
591
+ 'ue' => ['ü'],
592
+ 'uu' => ['ऊ'],
593
+ 'ya' => ['я'],
594
+ 'yu' => ['ю'],
595
+ 'zh' => ['ж', 'ჟ', 'ژ'],
596
+ '(c)' => ['©'],
597
+ 'A' => ['Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ', 'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Å', 'Ā', 'Ą', 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ', 'Ἇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ', 'Ᾱ', 'Ὰ', 'Ά', 'ᾼ', 'А', 'Ǻ', 'Ǎ'],
598
+ 'B' => ['Б', 'Β', 'ब'],
599
+ 'C' => ['Ç', 'Ć', 'Č', 'Ĉ', 'Ċ'],
600
+ 'D' => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ'],
601
+ 'E' => ['É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ', 'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ', 'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э', 'Є', 'Ə'],
602
+ 'F' => ['Ф', 'Φ'],
603
+ 'G' => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ'],
604
+ 'H' => ['Η', 'Ή', 'Ħ'],
605
+ 'I' => ['Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į', 'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ', 'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї', 'Ǐ', 'ϒ'],
606
+ 'K' => ['К', 'Κ'],
607
+ 'L' => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल'],
608
+ 'M' => ['М', 'Μ'],
609
+ 'N' => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν'],
610
+ 'O' => ['Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ', 'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ø', 'Ō', 'Ő', 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ', 'Ὸ', 'Ό', 'О', 'Θ', 'Ө', 'Ǒ', 'Ǿ'],
611
+ 'P' => ['П', 'Π'],
612
+ 'R' => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ'],
613
+ 'S' => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ'],
614
+ 'T' => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ'],
615
+ 'U' => ['Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ', 'Ự', 'Û', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У', 'Ǔ', 'Ǖ', 'Ǘ', 'Ǚ', 'Ǜ'],
616
+ 'V' => ['В'],
617
+ 'W' => ['Ω', 'Ώ', 'Ŵ'],
618
+ 'X' => ['Χ', 'Ξ'],
619
+ 'Y' => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ'],
620
+ 'Z' => ['Ź', 'Ž', 'Ż', 'З', 'Ζ'],
621
+ 'AE' => ['Ä', 'Æ', 'Ǽ'],
622
+ 'CH' => ['Ч'],
623
+ 'DJ' => ['Ђ'],
624
+ 'DZ' => ['Џ'],
625
+ 'GX' => ['Ĝ'],
626
+ 'HX' => ['Ĥ'],
627
+ 'IJ' => ['IJ'],
628
+ 'JX' => ['Ĵ'],
629
+ 'KH' => ['Х'],
630
+ 'LJ' => ['Љ'],
631
+ 'NJ' => ['Њ'],
632
+ 'OE' => ['Ö', 'Œ'],
633
+ 'PS' => ['Ψ'],
634
+ 'SH' => ['Ш'],
635
+ 'SHCH' => ['Щ'],
636
+ 'SS' => ['ẞ'],
637
+ 'TH' => ['Þ'],
638
+ 'TS' => ['Ц'],
639
+ 'UE' => ['Ü'],
640
+ 'YA' => ['Я'],
641
+ 'YU' => ['Ю'],
642
+ 'ZH' => ['Ж'],
643
+ ' ' => ["\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81", "\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84", "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87", "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A", "\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80"],
644
+ ];
645
+ }
646
+ }
vendor/illuminate/support/Testing/Fakes/BusFake.php ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Testing\Fakes;
4
+
5
+ use Illuminate\Contracts\Bus\Dispatcher;
6
+ use PHPUnit\Framework\Assert as PHPUnit;
7
+
8
+ class BusFake implements Dispatcher
9
+ {
10
+ /**
11
+ * The commands that have been dispatched.
12
+ *
13
+ * @var array
14
+ */
15
+ protected $commands = [];
16
+
17
+ /**
18
+ * Assert if a job was dispatched based on a truth-test callback.
19
+ *
20
+ * @param string $command
21
+ * @param callable|null $callback
22
+ * @return void
23
+ */
24
+ public function assertDispatched($command, $callback = null)
25
+ {
26
+ PHPUnit::assertTrue(
27
+ $this->dispatched($command, $callback)->count() > 0,
28
+ "The expected [{$command}] job was not dispatched."
29
+ );
30
+ }
31
+
32
+ /**
33
+ * Determine if a job was dispatched based on a truth-test callback.
34
+ *
35
+ * @param string $command
36
+ * @param callable|null $callback
37
+ * @return void
38
+ */
39
+ public function assertNotDispatched($command, $callback = null)
40
+ {
41
+ PHPUnit::assertTrue(
42
+ $this->dispatched($command, $callback)->count() === 0,
43
+ "The unexpected [{$command}] job was dispatched."
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Get all of the jobs matching a truth-test callback.
49
+ *
50
+ * @param string $command
51
+ * @param callable|null $callback
52
+ * @return \Illuminate\Support\Collection
53
+ */
54
+ public function dispatched($command, $callback = null)
55
+ {
56
+ if (! $this->hasDispatched($command)) {
57
+ return collect();
58
+ }
59
+
60
+ $callback = $callback ?: function () {
61
+ return true;
62
+ };
63
+
64
+ return collect($this->commands[$command])->filter(function ($command) use ($callback) {
65
+ return $callback($command);
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Determine if there are any stored commands for a given class.
71
+ *
72
+ * @param string $command
73
+ * @return bool
74
+ */
75
+ public function hasDispatched($command)
76
+ {
77
+ return isset($this->commands[$command]) && ! empty($this->commands[$command]);
78
+ }
79
+
80
+ /**
81
+ * Dispatch a command to its appropriate handler.
82
+ *
83
+ * @param mixed $command
84
+ * @return mixed
85
+ */
86
+ public function dispatch($command)
87
+ {
88
+ return $this->dispatchNow($command);
89
+ }
90
+
91
+ /**
92
+ * Dispatch a command to its appropriate handler in the current process.
93
+ *
94
+ * @param mixed $command
95
+ * @param mixed $handler
96
+ * @return mixed
97
+ */
98
+ public function dispatchNow($command, $handler = null)
99
+ {
100
+ $this->commands[get_class($command)][] = $command;
101
+ }
102
+
103
+ /**
104
+ * Set the pipes commands should be piped through before dispatching.
105
+ *
106
+ * @param array $pipes
107
+ * @return $this
108
+ */
109
+ public function pipeThrough(array $pipes)
110
+ {
111
+ //
112
+ }
113
+ }
vendor/illuminate/support/Testing/Fakes/EventFake.php ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Testing\Fakes;
4
+
5
+ use PHPUnit\Framework\Assert as PHPUnit;
6
+ use Illuminate\Contracts\Events\Dispatcher;
7
+
8
+ class EventFake implements Dispatcher
9
+ {
10
+ /**
11
+ * All of the events that have been dispatched keyed by type.
12
+ *
13
+ * @var array
14
+ */
15
+ protected $events = [];
16
+
17
+ /**
18
+ * Assert if an event was dispatched based on a truth-test callback.
19
+ *
20
+ * @param string $event
21
+ * @param callable|null $callback
22
+ * @return void
23
+ */
24
+ public function assertDispatched($event, $callback = null)
25
+ {
26
+ PHPUnit::assertTrue(
27
+ $this->dispatched($event, $callback)->count() > 0,
28
+ "The expected [{$event}] event was not dispatched."
29
+ );
30
+ }
31
+
32
+ /**
33
+ * Determine if an event was dispatched based on a truth-test callback.
34
+ *
35
+ * @param string $event
36
+ * @param callable|null $callback
37
+ * @return void
38
+ */
39
+ public function assertNotDispatched($event, $callback = null)
40
+ {
41
+ PHPUnit::assertTrue(
42
+ $this->dispatched($event, $callback)->count() === 0,
43
+ "The unexpected [{$event}] event was dispatched."
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Get all of the events matching a truth-test callback.
49
+ *
50
+ * @param string $event
51
+ * @param callable|null $callback
52
+ * @return \Illuminate\Support\Collection
53
+ */
54
+ public function dispatched($event, $callback = null)
55
+ {
56
+ if (! $this->hasDispatched($event)) {
57
+ return collect();
58
+ }
59
+
60
+ $callback = $callback ?: function () {
61
+ return true;
62
+ };
63
+
64
+ return collect($this->events[$event])->filter(function ($arguments) use ($callback) {
65
+ return $callback(...$arguments);
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Determine if the given event has been dispatched.
71
+ *
72
+ * @param string $event
73
+ * @return bool
74
+ */
75
+ public function hasDispatched($event)
76
+ {
77
+ return isset($this->events[$event]) && ! empty($this->events[$event]);
78
+ }
79
+
80
+ /**
81
+ * Register an event listener with the dispatcher.
82
+ *
83
+ * @param string|array $events
84
+ * @param mixed $listener
85
+ * @return void
86
+ */
87
+ public function listen($events, $listener)
88
+ {
89
+ //
90
+ }
91
+
92
+ /**
93
+ * Determine if a given event has listeners.
94
+ *
95
+ * @param string $eventName
96
+ * @return bool
97
+ */
98
+ public function hasListeners($eventName)
99
+ {
100
+ //
101
+ }
102
+
103
+ /**
104
+ * Register an event and payload to be dispatched later.
105
+ *
106
+ * @param string $event
107
+ * @param array $payload
108
+ * @return void
109
+ */
110
+ public function push($event, $payload = [])
111
+ {
112
+ //
113
+ }
114
+
115
+ /**
116
+ * Register an event subscriber with the dispatcher.
117
+ *
118
+ * @param object|string $subscriber
119
+ * @return void
120
+ */
121
+ public function subscribe($subscriber)
122
+ {
123
+ //
124
+ }
125
+
126
+ /**
127
+ * Flush a set of pushed events.
128
+ *
129
+ * @param string $event
130
+ * @return void
131
+ */
132
+ public function flush($event)
133
+ {
134
+ //
135
+ }
136
+
137
+ /**
138
+ * Fire an event and call the listeners.
139
+ *
140
+ * @param string|object $event
141
+ * @param mixed $payload
142
+ * @param bool $halt
143
+ * @return array|null
144
+ */
145
+ public function fire($event, $payload = [], $halt = false)
146
+ {
147
+ return $this->dispatch($event, $payload, $halt);
148
+ }
149
+
150
+ /**
151
+ * Fire an event and call the listeners.
152
+ *
153
+ * @param string|object $event
154
+ * @param mixed $payload
155
+ * @param bool $halt
156
+ * @return array|null
157
+ */
158
+ public function dispatch($event, $payload = [], $halt = false)
159
+ {
160
+ $name = is_object($event) ? get_class($event) : (string) $event;
161
+
162
+ $this->events[$name][] = func_get_args();
163
+ }
164
+
165
+ /**
166
+ * Remove a set of listeners from the dispatcher.
167
+ *
168
+ * @param string $event
169
+ * @return void
170
+ */
171
+ public function forget($event)
172
+ {
173
+ //
174
+ }
175
+
176
+ /**
177
+ * Forget all of the queued listeners.
178
+ *
179
+ * @return void
180
+ */
181
+ public function forgetPushed()
182
+ {
183
+ //
184
+ }
185
+
186
+ /**
187
+ * Dispatch an event and call the listeners.
188
+ *
189
+ * @param string|object $event
190
+ * @param mixed $payload
191
+ * @return void
192
+ */
193
+ public function until($event, $payload = [])
194
+ {
195
+ return $this->dispatch($event, $payload, true);
196
+ }
197
+ }
vendor/illuminate/support/Testing/Fakes/MailFake.php ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Testing\Fakes;
4
+
5
+ use Illuminate\Contracts\Mail\Mailer;
6
+ use Illuminate\Contracts\Mail\Mailable;
7
+ use PHPUnit\Framework\Assert as PHPUnit;
8
+
9
+ class MailFake implements Mailer
10
+ {
11
+ /**
12
+ * All of the mailables that have been sent.
13
+ *
14
+ * @var array
15
+ */
16
+ protected $mailables = [];
17
+
18
+ /**
19
+ * Assert if a mailable was sent based on a truth-test callback.
20
+ *
21
+ * @param string $mailable
22
+ * @param callable|null $callback
23
+ * @return void
24
+ */
25
+ public function assertSent($mailable, $callback = null)
26
+ {
27
+ PHPUnit::assertTrue(
28
+ $this->sent($mailable, $callback)->count() > 0,
29
+ "The expected [{$mailable}] mailable was not sent."
30
+ );
31
+ }
32
+
33
+ /**
34
+ * Determine if a mailable was not sent based on a truth-test callback.
35
+ *
36
+ * @param string $mailable
37
+ * @param callable|null $callback
38
+ * @return void
39
+ */
40
+ public function assertNotSent($mailable, $callback = null)
41
+ {
42
+ PHPUnit::assertTrue(
43
+ $this->sent($mailable, $callback)->count() === 0,
44
+ "The unexpected [{$mailable}] mailable was sent."
45
+ );
46
+ }
47
+
48
+ /**
49
+ * Assert that no mailables were sent.
50
+ *
51
+ * @return void
52
+ */
53
+ public function assertNothingSent()
54
+ {
55
+ PHPUnit::assertEmpty($this->mailables, 'Mailables were sent unexpectedly.');
56
+ }
57
+
58
+ /**
59
+ * Get all of the mailables matching a truth-test callback.
60
+ *
61
+ * @param string $mailable
62
+ * @param callable|null $callback
63
+ * @return \Illuminate\Support\Collection
64
+ */
65
+ public function sent($mailable, $callback = null)
66
+ {
67
+ if (! $this->hasSent($mailable)) {
68
+ return collect();
69
+ }
70
+
71
+ $callback = $callback ?: function () {
72
+ return true;
73
+ };
74
+
75
+ return $this->mailablesOf($mailable)->filter(function ($mailable) use ($callback) {
76
+ return $callback($mailable);
77
+ });
78
+ }
79
+
80
+ /**
81
+ * Determine if the given mailable has been sent.
82
+ *
83
+ * @param string $mailable
84
+ * @return bool
85
+ */
86
+ public function hasSent($mailable)
87
+ {
88
+ return $this->mailablesOf($mailable)->count() > 0;
89
+ }
90
+
91
+ /**
92
+ * Get all of the mailed mailables for a given type.
93
+ *
94
+ * @param string $type
95
+ * @return \Illuminate\Support\Collection
96
+ */
97
+ protected function mailablesOf($type)
98
+ {
99
+ return collect($this->mailables)->filter(function ($mailable) use ($type) {
100
+ return $mailable instanceof $type;
101
+ });
102
+ }
103
+
104
+ /**
105
+ * Begin the process of mailing a mailable class instance.
106
+ *
107
+ * @param mixed $users
108
+ * @return \Illuminate\Mail\PendingMail
109
+ */
110
+ public function to($users)
111
+ {
112
+ return (new PendingMailFake($this))->to($users);
113
+ }
114
+
115
+ /**
116
+ * Begin the process of mailing a mailable class instance.
117
+ *
118
+ * @param mixed $users
119
+ * @return \Illuminate\Mail\PendingMail
120
+ */
121
+ public function bcc($users)
122
+ {
123
+ return (new PendingMailFake($this))->bcc($users);
124
+ }
125
+
126
+ /**
127
+ * Send a new message when only a raw text part.
128
+ *
129
+ * @param string $text
130
+ * @param \Closure|string $callback
131
+ * @return int
132
+ */
133
+ public function raw($text, $callback)
134
+ {
135
+ //
136
+ }
137
+
138
+ /**
139
+ * Send a new message using a view.
140
+ *
141
+ * @param string|array $view
142
+ * @param array $data
143
+ * @param \Closure|string $callback
144
+ * @return void
145
+ */
146
+ public function send($view, array $data = [], $callback = null)
147
+ {
148
+ if (! $view instanceof Mailable) {
149
+ return;
150
+ }
151
+
152
+ $this->mailables[] = $view;
153
+ }
154
+
155
+ /**
156
+ * Queue a new e-mail message for sending.
157
+ *
158
+ * @param string|array $view
159
+ * @param array $data
160
+ * @param \Closure|string $callback
161
+ * @param string|null $queue
162
+ * @return mixed
163
+ */
164
+ public function queue($view, array $data = [], $callback = null, $queue = null)
165
+ {
166
+ $this->send($view);
167
+ }
168
+
169
+ /**
170
+ * Get the array of failed recipients.
171
+ *
172
+ * @return array
173
+ */
174
+ public function failures()
175
+ {
176
+ //
177
+ }
178
+ }
vendor/illuminate/support/Testing/Fakes/NotificationFake.php ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Testing\Fakes;
4
+
5
+ use Ramsey\Uuid\Uuid;
6
+ use Illuminate\Support\Collection;
7
+ use PHPUnit\Framework\Assert as PHPUnit;
8
+ use Illuminate\Contracts\Notifications\Factory as NotificationFactory;
9
+
10
+ class NotificationFake implements NotificationFactory
11
+ {
12
+ /**
13
+ * All of the notifications that have been sent.
14
+ *
15
+ * @var array
16
+ */
17
+ protected $notifications = [];
18
+
19
+ /**
20
+ * Assert if a notification was sent based on a truth-test callback.
21
+ *
22
+ * @param mixed $notifiable
23
+ * @param string $notification
24
+ * @param callable|null $callback
25
+ * @return void
26
+ */
27
+ public function assertSentTo($notifiable, $notification, $callback = null)
28
+ {
29
+ if (is_array($notifiable) || $notifiable instanceof Collection) {
30
+ foreach ($notifiable as $singleNotifiable) {
31
+ $this->assertSentTo($singleNotifiable, $notification, $callback);
32
+ }
33
+
34
+ return;
35
+ }
36
+
37
+ PHPUnit::assertTrue(
38
+ $this->sent($notifiable, $notification, $callback)->count() > 0,
39
+ "The expected [{$notification}] notification was not sent."
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Determine if a notification was sent based on a truth-test callback.
45
+ *
46
+ * @param mixed $notifiable
47
+ * @param string $notification
48
+ * @param callable|null $callback
49
+ * @return void
50
+ */
51
+ public function assertNotSentTo($notifiable, $notification, $callback = null)
52
+ {
53
+ if (is_array($notifiable) || $notifiable instanceof Collection) {
54
+ foreach ($notifiable as $singleNotifiable) {
55
+ $this->assertNotSentTo($singleNotifiable, $notification, $callback);
56
+ }
57
+
58
+ return;
59
+ }
60
+
61
+ PHPUnit::assertTrue(
62
+ $this->sent($notifiable, $notification, $callback)->count() === 0,
63
+ "The unexpected [{$notification}] notification was sent."
64
+ );
65
+ }
66
+
67
+ /**
68
+ * Get all of the notifications matching a truth-test callback.
69
+ *
70
+ * @param mixed $notifiable
71
+ * @param string $notification
72
+ * @param callable|null $callback
73
+ * @return \Illuminate\Support\Collection
74
+ */
75
+ public function sent($notifiable, $notification, $callback = null)
76
+ {
77
+ if (! $this->hasSent($notifiable, $notification)) {
78
+ return collect();
79
+ }
80
+
81
+ $callback = $callback ?: function () {
82
+ return true;
83
+ };
84
+
85
+ $notifications = collect($this->notificationsFor($notifiable, $notification));
86
+
87
+ return $notifications->filter(function ($arguments) use ($callback) {
88
+ return $callback(...array_values($arguments));
89
+ })->pluck('notification');
90
+ }
91
+
92
+ /**
93
+ * Determine if there are more notifications left to inspect.
94
+ *
95
+ * @param mixed $notifiable
96
+ * @param string $notification
97
+ * @return bool
98
+ */
99
+ public function hasSent($notifiable, $notification)
100
+ {
101
+ return ! empty($this->notificationsFor($notifiable, $notification));
102
+ }
103
+
104
+ /**
105
+ * Get all of the notifications for a notifiable entity by type.
106
+ *
107
+ * @param mixed $notifiable
108
+ * @param string $notification
109
+ * @return array
110
+ */
111
+ protected function notificationsFor($notifiable, $notification)
112
+ {
113
+ if (isset($this->notifications[get_class($notifiable)][$notifiable->getKey()][$notification])) {
114
+ return $this->notifications[get_class($notifiable)][$notifiable->getKey()][$notification];
115
+ }
116
+
117
+ return [];
118
+ }
119
+
120
+ /**
121
+ * Send the given notification to the given notifiable entities.
122
+ *
123
+ * @param \Illuminate\Support\Collection|array|mixed $notifiables
124
+ * @param mixed $notification
125
+ * @return void
126
+ */
127
+ public function send($notifiables, $notification)
128
+ {
129
+ return $this->sendNow($notifiables, $notification);
130
+ }
131
+
132
+ /**
133
+ * Send the given notification immediately.
134
+ *
135
+ * @param \Illuminate\Support\Collection|array|mixed $notifiables
136
+ * @param mixed $notification
137
+ * @return void
138
+ */
139
+ public function sendNow($notifiables, $notification)
140
+ {
141
+ if (! $notifiables instanceof Collection && ! is_array($notifiables)) {
142
+ $notifiables = [$notifiables];
143
+ }
144
+
145
+ foreach ($notifiables as $notifiable) {
146
+ $notification->id = Uuid::uuid4()->toString();
147
+
148
+ $this->notifications[get_class($notifiable)][$notifiable->getKey()][get_class($notification)][] = [
149
+ 'notification' => $notification,
150
+ 'channels' => $notification->via($notifiable),
151
+ ];
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Get a channel instance by name.
157
+ *
158
+ * @param string|null $name
159
+ * @return mixed
160
+ */
161
+ public function channel($name = null)
162
+ {
163
+ //
164
+ }
165
+ }
vendor/illuminate/support/Testing/Fakes/PendingMailFake.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Testing\Fakes;
4
+
5
+ use Illuminate\Mail\Mailable;
6
+ use Illuminate\Mail\PendingMail;
7
+
8
+ class PendingMailFake extends PendingMail
9
+ {
10
+ /**
11
+ * Create a new instance.
12
+ *
13
+ * @param \Illuminate\Support\Testing\Fakes\MailFake $mailer
14
+ * @return void
15
+ */
16
+ public function __construct($mailer)
17
+ {
18
+ $this->mailer = $mailer;
19
+ }
20
+
21
+ /**
22
+ * Send a new mailable message instance.
23
+ *
24
+ * @param Mailable $mailable
25
+ * @return mixed
26
+ */
27
+ public function send(Mailable $mailable)
28
+ {
29
+ return $this->sendNow($mailable);
30
+ }
31
+
32
+ /**
33
+ * Send a mailable message immediately.
34
+ *
35
+ * @param Mailable $mailable
36
+ * @return mixed
37
+ */
38
+ public function sendNow(Mailable $mailable)
39
+ {
40
+ $this->mailer->send($this->fill($mailable));
41
+ }
42
+
43
+ /**
44
+ * Push the given mailable onto the queue.
45
+ *
46
+ * @param Mailable $mailable
47
+ * @return mixed
48
+ */
49
+ public function queue(Mailable $mailable)
50
+ {
51
+ return $this->sendNow($mailable);
52
+ }
53
+ }
vendor/illuminate/support/Testing/Fakes/QueueFake.php ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Testing\Fakes;
4
+
5
+ use Illuminate\Queue\QueueManager;
6
+ use Illuminate\Contracts\Queue\Queue;
7
+ use PHPUnit\Framework\Assert as PHPUnit;
8
+
9
+ class QueueFake extends QueueManager implements Queue
10
+ {
11
+ /**
12
+ * All of the jobs that have been pushed.
13
+ *
14
+ * @var array
15
+ */
16
+ protected $jobs = [];
17
+
18
+ /**
19
+ * Assert if a job was pushed based on a truth-test callback.
20
+ *
21
+ * @param string $job
22
+ * @param callable|null $callback
23
+ * @return void
24
+ */
25
+ public function assertPushed($job, $callback = null)
26
+ {
27
+ PHPUnit::assertTrue(
28
+ $this->pushed($job, $callback)->count() > 0,
29
+ "The expected [{$job}] job was not pushed."
30
+ );
31
+ }
32
+
33
+ /**
34
+ * Assert if a job was pushed based on a truth-test callback.
35
+ *
36
+ * @param string $queue
37
+ * @param string $job
38
+ * @param callable|null $callback
39
+ * @return void
40
+ */
41
+ public function assertPushedOn($queue, $job, $callback = null)
42
+ {
43
+ return $this->assertPushed($job, function ($job, $pushedQueue) use ($callback, $queue) {
44
+ if ($pushedQueue !== $queue) {
45
+ return false;
46
+ }
47
+
48
+ return $callback ? $callback(...func_get_args()) : true;
49
+ });
50
+ }
51
+
52
+ /**
53
+ * Determine if a job was pushed based on a truth-test callback.
54
+ *
55
+ * @param string $job
56
+ * @param callable|null $callback
57
+ * @return void
58
+ */
59
+ public function assertNotPushed($job, $callback = null)
60
+ {
61
+ PHPUnit::assertTrue(
62
+ $this->pushed($job, $callback)->count() === 0,
63
+ "The unexpected [{$job}] job was pushed."
64
+ );
65
+ }
66
+
67
+ /**
68
+ * Get all of the jobs matching a truth-test callback.
69
+ *
70
+ * @param string $job
71
+ * @param callable|null $callback
72
+ * @return \Illuminate\Support\Collection
73
+ */
74
+ public function pushed($job, $callback = null)
75
+ {
76
+ if (! $this->hasPushed($job)) {
77
+ return collect();
78
+ }
79
+
80
+ $callback = $callback ?: function () {
81
+ return true;
82
+ };
83
+
84
+ return collect($this->jobs[$job])->filter(function ($data) use ($callback) {
85
+ return $callback($data['job'], $data['queue']);
86
+ })->pluck('job');
87
+ }
88
+
89
+ /**
90
+ * Determine if there are any stored jobs for a given class.
91
+ *
92
+ * @param string $job
93
+ * @return bool
94
+ */
95
+ public function hasPushed($job)
96
+ {
97
+ return isset($this->jobs[$job]) && ! empty($this->jobs[$job]);
98
+ }
99
+
100
+ /**
101
+ * Resolve a queue connection instance.
102
+ *
103
+ * @param mixed $value
104
+ * @return \Illuminate\Contracts\Queue\Queue
105
+ */
106
+ public function connection($value = null)
107
+ {
108
+ return $this;
109
+ }
110
+
111
+ /**
112
+ * Get the size of the queue.
113
+ *
114
+ * @param string $queue
115
+ * @return int
116
+ */
117
+ public function size($queue = null)
118
+ {
119
+ return 0;
120
+ }
121
+
122
+ /**
123
+ * Push a new job onto the queue.
124
+ *
125
+ * @param string $job
126
+ * @param mixed $data
127
+ * @param string $queue
128
+ * @return mixed
129
+ */
130
+ public function push($job, $data = '', $queue = null)
131
+ {
132
+ $this->jobs[is_object($job) ? get_class($job) : $job][] = [
133
+ 'job' => $job,
134
+ 'queue' => $queue,
135
+ ];
136
+ }
137
+
138
+ /**
139
+ * Push a raw payload onto the queue.
140
+ *
141
+ * @param string $payload
142
+ * @param string $queue
143
+ * @param array $options
144
+ * @return mixed
145
+ */
146
+ public function pushRaw($payload, $queue = null, array $options = [])
147
+ {
148
+ //
149
+ }
150
+
151
+ /**
152
+ * Push a new job onto the queue after a delay.
153
+ *
154
+ * @param \DateTime|int $delay
155
+ * @param string $job
156
+ * @param mixed $data
157
+ * @param string $queue
158
+ * @return mixed
159
+ */
160
+ public function later($delay, $job, $data = '', $queue = null)
161
+ {
162
+ return $this->push($job, $data, $queue);
163
+ }
164
+
165
+ /**
166
+ * Push a new job onto the queue.
167
+ *
168
+ * @param string $queue
169
+ * @param string $job
170
+ * @param mixed $data
171
+ * @return mixed
172
+ */
173
+ public function pushOn($queue, $job, $data = '')
174
+ {
175
+ return $this->push($job, $data, $queue);
176
+ }
177
+
178
+ /**
179
+ * Push a new job onto the queue after a delay.
180
+ *
181
+ * @param string $queue
182
+ * @param \DateTime|int $delay
183
+ * @param string $job
184
+ * @param mixed $data
185
+ * @return mixed
186
+ */
187
+ public function laterOn($queue, $delay, $job, $data = '')
188
+ {
189
+ return $this->push($job, $data, $queue);
190
+ }
191
+
192
+ /**
193
+ * Pop the next job off of the queue.
194
+ *
195
+ * @param string $queue
196
+ * @return \Illuminate\Contracts\Queue\Job|null
197
+ */
198
+ public function pop($queue = null)
199
+ {
200
+ //
201
+ }
202
+
203
+ /**
204
+ * Push an array of jobs onto the queue.
205
+ *
206
+ * @param array $jobs
207
+ * @param mixed $data
208
+ * @param string $queue
209
+ * @return mixed
210
+ */
211
+ public function bulk($jobs, $data = '', $queue = null)
212
+ {
213
+ foreach ($this->jobs as $job) {
214
+ $this->push($job);
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Get the connection name for the queue.
220
+ *
221
+ * @return string
222
+ */
223
+ public function getConnectionName()
224
+ {
225
+ //
226
+ }
227
+
228
+ /**
229
+ * Set the connection name for the queue.
230
+ *
231
+ * @param string $name
232
+ * @return $this
233
+ */
234
+ public function setConnectionName($name)
235
+ {
236
+ return $this;
237
+ }
238
+ }
vendor/illuminate/support/Traits/CapsuleManagerTrait.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Traits;
4
+
5
+ use Illuminate\Support\Fluent;
6
+ use Illuminate\Contracts\Container\Container;
7
+
8
+ trait CapsuleManagerTrait
9
+ {
10
+ /**
11
+ * The current globally used instance.
12
+ *
13
+ * @var object
14
+ */
15
+ protected static $instance;
16
+
17
+ /**
18
+ * The container instance.
19
+ *
20
+ * @var \Illuminate\Contracts\Container\Container
21
+ */
22
+ protected $container;
23
+
24
+ /**
25
+ * Setup the IoC container instance.
26
+ *
27
+ * @param \Illuminate\Contracts\Container\Container $container
28
+ * @return void
29
+ */
30
+ protected function setupContainer(Container $container)
31
+ {
32
+ $this->container = $container;
33
+
34
+ if (! $this->container->bound('config')) {
35
+ $this->container->instance('config', new Fluent);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Make this capsule instance available globally.
41
+ *
42
+ * @return void
43
+ */
44
+ public function setAsGlobal()
45
+ {
46
+ static::$instance = $this;
47
+ }
48
+
49
+ /**
50
+ * Get the IoC container instance.
51
+ *
52
+ * @return \Illuminate\Contracts\Container\Container
53
+ */
54
+ public function getContainer()
55
+ {
56
+ return $this->container;
57
+ }
58
+
59
+ /**
60
+ * Set the IoC container instance.
61
+ *
62
+ * @param \Illuminate\Contracts\Container\Container $container
63
+ * @return void
64
+ */
65
+ public function setContainer(Container $container)
66
+ {
67
+ $this->container = $container;
68
+ }
69
+ }
vendor/illuminate/support/Traits/Macroable.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support\Traits;
4
+
5
+ use Closure;
6
+ use BadMethodCallException;
7
+
8
+ trait Macroable
9
+ {
10
+ /**
11
+ * The registered string macros.
12
+ *
13
+ * @var array
14
+ */
15
+ protected static $macros = [];
16
+
17
+ /**
18
+ * Register a custom macro.
19
+ *
20
+ * @param string $name
21
+ * @param callable $macro
22
+ * @return void
23
+ */
24
+ public static function macro($name, callable $macro)
25
+ {
26
+ static::$macros[$name] = $macro;
27
+ }
28
+
29
+ /**
30
+ * Checks if macro is registered.
31
+ *
32
+ * @param string $name
33
+ * @return bool
34
+ */
35
+ public static function hasMacro($name)
36
+ {
37
+ return isset(static::$macros[$name]);
38
+ }
39
+
40
+ /**
41
+ * Dynamically handle calls to the class.
42
+ *
43
+ * @param string $method
44
+ * @param array $parameters
45
+ * @return mixed
46
+ *
47
+ * @throws \BadMethodCallException
48
+ */
49
+ public static function __callStatic($method, $parameters)
50
+ {
51
+ if (! static::hasMacro($method)) {
52
+ throw new BadMethodCallException("Method {$method} does not exist.");
53
+ }
54
+
55
+ if (static::$macros[$method] instanceof Closure) {
56
+ return call_user_func_array(Closure::bind(static::$macros[$method], null, static::class), $parameters);
57
+ }
58
+
59
+ return call_user_func_array(static::$macros[$method], $parameters);
60
+ }
61
+
62
+ /**
63
+ * Dynamically handle calls to the class.
64
+ *
65
+ * @param string $method
66
+ * @param array $parameters
67
+ * @return mixed
68
+ *
69
+ * @throws \BadMethodCallException
70
+ */
71
+ public function __call($method, $parameters)
72
+ {
73
+ if (! static::hasMacro($method)) {
74
+ throw new BadMethodCallException("Method {$method} does not exist.");
75
+ }
76
+
77
+ if (static::$macros[$method] instanceof Closure) {
78
+ return call_user_func_array(static::$macros[$method]->bindTo($this, static::class), $parameters);
79
+ }
80
+
81
+ return call_user_func_array(static::$macros[$method], $parameters);
82
+ }
83
+ }
vendor/illuminate/support/ViewErrorBag.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Illuminate\Support;
4
+
5
+ use Countable;
6
+ use Illuminate\Contracts\Support\MessageBag as MessageBagContract;
7
+
8
+ /**
9
+ * @mixin \Illuminate\Contracts\Support\MessageBag
10
+ */
11
+ class ViewErrorBag implements Countable
12
+ {
13
+ /**
14
+ * The array of the view error bags.
15
+ *
16
+ * @var array
17
+ */
18
+ protected $bags = [];
19
+
20
+ /**
21
+ * Checks if a named MessageBag exists in the bags.
22
+ *
23
+ * @param string $key
24
+ * @return bool
25
+ */
26
+ public function hasBag($key = 'default')
27
+ {
28
+ return isset($this->bags[$key]);
29
+ }
30
+
31
+ /**
32
+ * Get a MessageBag instance from the bags.
33
+ *
34
+ * @param string $key
35
+ * @return \Illuminate\Contracts\Support\MessageBag
36
+ */
37
+ public function getBag($key)
38
+ {
39
+ return Arr::get($this->bags, $key) ?: new MessageBag;
40
+ }
41
+
42
+ /**
43
+ * Get all the bags.
44
+ *
45
+ * @return array
46
+ */
47
+ public function getBags()
48
+ {
49
+ return $this->bags;
50
+ }
51
+
52
+ /**
53
+ * Add a new MessageBag instance to the bags.
54
+ *
55
+ * @param string $key
56
+ * @param \Illuminate\Contracts\Support\MessageBag $bag
57
+ * @return $this
58
+ */
59
+ public function put($key, MessageBagContract $bag)
60
+ {
61
+ $this->bags[$key] = $bag;
62
+
63
+ return $this;
64
+ }
65
+
66
+ /**
67
+ * Determine if the default message bag has any messages.
68
+ *
69
+ * @return bool
70
+ */
71
+ public function any()
72
+ {
73
+ return $this->count() > 0;
74
+ }
75
+
76
+ /**
77
+ * Get the number of messages in the default bag.
78
+ *
79
+ * @return int
80
+ */
81
+ public function count()
82
+ {
83
+ return $this->getBag('default')->count();
84
+ }
85
+
86
+ /**
87
+ * Dynamically call methods on the default bag.
88
+ *
89
+ * @param string $method
90
+ * @param array $parameters
91
+ * @return mixed
92
+ */
93
+ public function __call($method, $parameters)
94
+ {
95
+ return $this->getBag('default')->$method(...$parameters);
96
+ }
97
+
98
+ /**
99
+ * Dynamically access a view error bag.
100
+ *
101
+ * @param string $key
102
+ * @return \Illuminate\Contracts\Support\MessageBag
103
+ */
104
+ public function __get($key)
105
+ {
106
+ return $this->getBag($key);
107
+ }
108
+
109
+ /**
110
+ * Dynamically set a view error bag.
111
+ *
112
+ * @param string $key
113
+ * @param \Illuminate\Contracts\Support\MessageBag $value
114
+ * @return void
115
+ */
116
+ public function __set($key, $value)
117
+ {
118
+ $this->put($key, $value);
119
+ }
120
+ }
vendor/illuminate/support/composer.json ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "illuminate/support",
3
+ "description": "The Illuminate Support package.",
4
+ "license": "MIT",
5
+ "homepage": "https://laravel.com",
6
+ "support": {
7
+ "issues": "https://github.com/laravel/framework/issues",
8
+ "source": "https://github.com/laravel/framework"
9
+ },
10
+ "authors": [
11
+ {
12
+ "name": "Taylor Otwell",
13
+ "email": "taylor@laravel.com"
14
+ }
15
+ ],
16
+ "require": {
17
+ "php": ">=5.6.4",
18
+ "ext-mbstring": "*",
19
+ "doctrine/inflector": "~1.1",
20
+ "illuminate/contracts": "5.4.*",
21
+ "paragonie/random_compat": "~1.4|~2.0"
22
+ },
23
+ "replace": {
24
+ "tightenco/collect": "self.version"
25
+ },
26
+ "autoload": {
27
+ "psr-4": {
28
+ "Illuminate\\Support\\": ""
29
+ },
30
+ "files": [
31
+ "helpers.php"
32
+ ]
33
+ },
34
+ "extra": {
35
+ "branch-alias": {
36
+ "dev-master": "5.4-dev"
37
+ }
38
+ },
39
+ "suggest": {
40
+ "illuminate/filesystem": "Required to use the composer class (5.2.*).",
41
+ "symfony/process": "Required to use the composer class (~3.2).",
42
+ "symfony/var-dumper": "Required to use the dd function (~3.2)."
43
+ },
44
+ "config": {
45
+ "sort-packages": true
46
+ },
47
+ "minimum-stability": "dev"
48
+ }
vendor/illuminate/support/helpers.php ADDED
@@ -0,0 +1,1038 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use Illuminate\Support\Arr;
4
+ use Illuminate\Support\Str;
5
+ use Illuminate\Support\Collection;
6
+ use Illuminate\Support\Debug\Dumper;
7
+ use Illuminate\Contracts\Support\Htmlable;
8
+ use Illuminate\Support\HigherOrderTapProxy;
9
+
10
+ if (! function_exists('append_config')) {
11
+ /**
12
+ * Assign high numeric IDs to a config item to force appending.
13
+ *
14
+ * @param array $array
15
+ * @return array
16
+ */
17
+ function append_config(array $array)
18
+ {
19
+ $start = 9999;
20
+
21
+ foreach ($array as $key => $value) {
22
+ if (is_numeric($key)) {
23
+ $start++;
24
+
25
+ $array[$start] = Arr::pull($array, $key);
26
+ }
27
+ }
28
+
29
+ return $array;
30
+ }
31
+ }
32
+
33
+ if (! function_exists('array_add')) {
34
+ /**
35
+ * Add an element to an array using "dot" notation if it doesn't exist.
36
+ *
37
+ * @param array $array
38
+ * @param string $key
39
+ * @param mixed $value
40
+ * @return array
41
+ */
42
+ function array_add($array, $key, $value)
43
+ {
44
+ return Arr::add($array, $key, $value);
45
+ }
46
+ }
47
+
48
+ if (! function_exists('array_collapse')) {
49
+ /**
50
+ * Collapse an array of arrays into a single array.
51
+ *
52
+ * @param array $array
53
+ * @return array
54
+ */
55
+ function array_collapse($array)
56
+ {
57
+ return Arr::collapse($array);
58
+ }
59
+ }
60
+
61
+ if (! function_exists('array_divide')) {
62
+ /**
63
+ * Divide an array into two arrays. One with keys and the other with values.
64
+ *
65
+ * @param array $array
66
+ * @return array
67
+ */
68
+ function array_divide($array)
69
+ {
70
+ return Arr::divide($array);
71
+ }
72
+ }
73
+
74
+ if (! function_exists('array_dot')) {
75
+ /**
76
+ * Flatten a multi-dimensional associative array with dots.
77
+ *
78
+ * @param array $array
79
+ * @param string $prepend
80
+ * @return array
81
+ */
82
+ function array_dot($array, $prepend = '')
83
+ {
84
+ return Arr::dot($array, $prepend);
85
+ }
86
+ }
87
+
88
+ if (! function_exists('array_except')) {
89
+ /**
90
+ * Get all of the given array except for a specified array of items.
91
+ *
92
+ * @param array $array
93
+ * @param array|string $keys
94
+ * @return array
95
+ */
96
+ function array_except($array, $keys)
97
+ {
98
+ return Arr::except($array, $keys);
99
+ }
100
+ }
101
+
102
+ if (! function_exists('array_first')) {
103
+ /**
104
+ * Return the first element in an array passing a given truth test.
105
+ *
106
+ * @param array $array
107
+ * @param callable|null $callback
108
+ * @param mixed $default
109
+ * @return mixed
110
+ */
111
+ function array_first($array, callable $callback = null, $default = null)
112
+ {
113
+ return Arr::first($array, $callback, $default);
114
+ }
115
+ }
116
+
117
+ if (! function_exists('array_flatten')) {
118
+ /**
119
+ * Flatten a multi-dimensional array into a single level.
120
+ *
121
+ * @param array $array
122
+ * @param int $depth
123
+ * @return array
124
+ */
125
+ function array_flatten($array, $depth = INF)
126
+ {
127
+ return Arr::flatten($array, $depth);
128
+ }
129
+ }
130
+
131
+ if (! function_exists('array_forget')) {
132
+ /**
133
+ * Remove one or many array items from a given array using "dot" notation.
134
+ *
135
+ * @param array $array
136
+ * @param array|string $keys
137
+ * @return void
138
+ */
139
+ function array_forget(&$array, $keys)
140
+ {
141
+ return Arr::forget($array, $keys);
142
+ }
143
+ }
144
+
145
+ if (! function_exists('array_get')) {
146
+ /**
147
+ * Get an item from an array using "dot" notation.
148
+ *
149
+ * @param \ArrayAccess|array $array
150
+ * @param string $key
151
+ * @param mixed $default
152
+ * @return mixed
153
+ */
154
+ function array_get($array, $key, $default = null)
155
+ {
156
+ return Arr::get($array, $key, $default);
157
+ }
158
+ }
159
+
160
+ if (! function_exists('array_has')) {
161
+ /**
162
+ * Check if an item or items exist in an array using "dot" notation.
163
+ *
164
+ * @param \ArrayAccess|array $array
165
+ * @param string|array $keys
166
+ * @return bool
167
+ */
168
+ function array_has($array, $keys)
169
+ {
170
+ return Arr::has($array, $keys);
171
+ }
172
+ }
173
+
174
+ if (! function_exists('array_last')) {
175
+ /**
176
+ * Return the last element in an array passing a given truth test.
177
+ *
178
+ * @param array $array
179
+ * @param callable|null $callback
180
+ * @param mixed $default
181
+ * @return mixed
182
+ */
183
+ function array_last($array, callable $callback = null, $default = null)
184
+ {
185
+ return Arr::last($array, $callback, $default);
186
+ }
187
+ }
188
+
189
+ if (! function_exists('array_only')) {
190
+ /**
191
+ * Get a subset of the items from the given array.
192
+ *
193
+ * @param array $array
194
+ * @param array|string $keys
195
+ * @return array
196
+ */
197
+ function array_only($array, $keys)
198
+ {
199
+ return Arr::only($array, $keys);
200
+ }
201
+ }
202
+
203
+ if (! function_exists('array_pluck')) {
204
+ /**
205
+ * Pluck an array of values from an array.
206
+ *
207
+ * @param array $array
208
+ * @param string|array $value
209
+ * @param string|array|null $key
210
+ * @return array
211
+ */
212
+ function array_pluck($array, $value, $key = null)
213
+ {
214
+ return Arr::pluck($array, $value, $key);
215
+ }
216
+ }
217
+
218
+ if (! function_exists('array_prepend')) {
219
+ /**
220
+ * Push an item onto the beginning of an array.
221
+ *
222
+ * @param array $array
223
+ * @param mixed $value
224
+ * @param mixed $key
225
+ * @return array
226
+ */
227
+ function array_prepend($array, $value, $key = null)
228
+ {
229
+ return Arr::prepend($array, $value, $key);
230
+ }
231
+ }
232
+
233
+ if (! function_exists('array_pull')) {
234
+ /**
235
+ * Get a value from the array, and remove it.
236
+ *
237
+ * @param array $array
238
+ * @param string $key
239
+ * @param mixed $default
240
+ * @return mixed
241
+ */
242
+ function array_pull(&$array, $key, $default = null)
243
+ {
244
+ return Arr::pull($array, $key, $default);
245
+ }
246
+ }
247
+
248
+ if (! function_exists('array_random')) {
249
+ /**
250
+ * Get a random value from an array.
251
+ *
252
+ * @param array $array
253
+ * @param int|null $num
254
+ * @return mixed
255
+ */
256
+ function array_random($array, $num = null)
257
+ {
258
+ return Arr::random($array, $num);
259
+ }
260
+ }
261
+
262
+ if (! function_exists('array_set')) {
263
+ /**
264
+ * Set an array item to a given value using "dot" notation.
265
+ *
266
+ * If no key is given to the method, the entire array will be replaced.
267
+ *
268
+ * @param array $array
269
+ * @param string $key
270
+ * @param mixed $value
271
+ * @return array
272
+ */
273
+ function array_set(&$array, $key, $value)
274
+ {
275
+ return Arr::set($array, $key, $value);
276
+ }
277
+ }
278
+
279
+ if (! function_exists('array_sort')) {
280
+ /**
281
+ * Sort the array by the given callback or attribute name.
282
+ *
283
+ * @param array $array
284
+ * @param callable|string $callback
285
+ * @return array
286
+ */
287
+ function array_sort($array, $callback)
288
+ {
289
+ return Arr::sort($array, $callback);
290
+ }
291
+ }
292
+
293
+ if (! function_exists('array_sort_recursive')) {
294
+ /**
295
+ * Recursively sort an array by keys and values.
296
+ *
297
+ * @param array $array
298
+ * @return array
299
+ */
300
+ function array_sort_recursive($array)
301
+ {
302
+ return Arr::sortRecursive($array);
303
+ }
304
+ }
305
+
306
+ if (! function_exists('array_where')) {
307
+ /**
308
+ * Filter the array using the given callback.
309
+ *
310
+ * @param array $array
311
+ * @param callable $callback
312
+ * @return array
313
+ */
314
+ function array_where($array, callable $callback)
315
+ {
316
+ return Arr::where($array, $callback);
317
+ }
318
+ }
319
+
320
+ if (! function_exists('array_wrap')) {
321
+ /**
322
+ * If the given value is not an array, wrap it in one.
323
+ *
324
+ * @param mixed $value
325
+ * @return array
326
+ */
327
+ function array_wrap($value)
328
+ {
329
+ return Arr::wrap($value);
330
+ }
331
+ }
332
+
333
+ if (! function_exists('camel_case')) {
334
+ /**
335
+ * Convert a value to camel case.
336
+ *
337
+ * @param string $value
338
+ * @return string
339
+ */
340
+ function camel_case($value)
341
+ {
342
+ return Str::camel($value);
343
+ }
344
+ }
345
+
346
+ if (! function_exists('class_basename')) {
347
+ /**
348
+ * Get the class "basename" of the given object / class.
349
+ *
350
+ * @param string|object $class
351
+ * @return string
352
+ */
353
+ function class_basename($class)
354
+ {
355
+ $class = is_object($class) ? get_class($class) : $class;
356
+
357
+ return basename(str_replace('\\', '/', $class));
358
+ }
359
+ }
360
+
361
+ if (! function_exists('class_uses_recursive')) {
362
+ /**
363
+ * Returns all traits used by a class, its subclasses and trait of their traits.
364
+ *
365
+ * @param object|string $class
366
+ * @return array
367
+ */
368
+ function class_uses_recursive($class)
369
+ {
370
+ if (is_object($class)) {
371
+ $class = get_class($class);
372
+ }
373
+
374
+ $results = [];
375
+
376
+ foreach (array_merge([$class => $class], class_parents($class)) as $class) {
377
+ $results += trait_uses_recursive($class);
378
+ }
379
+
380
+ return array_unique($results);
381
+ }
382
+ }
383
+
384
+ if (! function_exists('collect')) {
385
+ /**
386
+ * Create a collection from the given value.
387
+ *
388
+ * @param mixed $value
389
+ * @return \Illuminate\Support\Collection
390
+ */
391
+ function collect($value = null)
392
+ {
393
+ return new Collection($value);
394
+ }
395
+ }
396
+
397
+ if (! function_exists('data_fill')) {
398
+ /**
399
+ * Fill in data where it's missing.
400
+ *
401
+ * @param mixed $target
402
+ * @param string|array $key
403
+ * @param mixed $value
404
+ * @return mixed
405
+ */
406
+ function data_fill(&$target, $key, $value)
407
+ {
408
+ return data_set($target, $key, $value, false);
409
+ }
410
+ }
411
+
412
+ if (! function_exists('data_get')) {
413
+ /**
414
+ * Get an item from an array or object using "dot" notation.
415
+ *
416
+ * @param mixed $target
417
+ * @param string|array $key
418
+ * @param mixed $default
419
+ * @return mixed
420
+ */
421
+ function data_get($target, $key, $default = null)
422
+ {
423
+ if (is_null($key)) {
424
+ return $target;
425
+ }
426
+
427
+ $key = is_array($key) ? $key : explode('.', $key);
428
+
429
+ while (! is_null($segment = array_shift($key))) {
430
+ if ($segment === '*') {
431
+ if ($target instanceof Collection) {
432
+ $target = $target->all();
433
+ } elseif (! is_array($target)) {
434
+ return value($default);
435
+ }
436
+
437
+ $result = Arr::pluck($target, $key);
438
+
439
+ return in_array('*', $key) ? Arr::collapse($result) : $result;
440
+ }
441
+
442
+ if (Arr::accessible($target) && Arr::exists($target, $segment)) {
443
+ $target = $target[$segment];
444
+ } elseif (is_object($target) && isset($target->{$segment})) {
445
+ $target = $target->{$segment};
446
+ } else {
447
+ return value($default);
448
+ }
449
+ }
450
+
451
+ return $target;
452
+ }
453
+ }
454
+
455
+ if (! function_exists('data_set')) {
456
+ /**
457
+ * Set an item on an array or object using dot notation.
458
+ *
459
+ * @param mixed $target
460
+ * @param string|array $key
461
+ * @param mixed $value
462
+ * @param bool $overwrite
463
+ * @return mixed
464
+ */
465
+ function data_set(&$target, $key, $value, $overwrite = true)
466
+ {
467
+ $segments = is_array($key) ? $key : explode('.', $key);
468
+
469
+ if (($segment = array_shift($segments)) === '*') {
470
+ if (! Arr::accessible($target)) {
471
+ $target = [];
472
+ }
473
+
474
+ if ($segments) {
475
+ foreach ($target as &$inner) {
476
+ data_set($inner, $segments, $value, $overwrite);
477
+ }
478
+ } elseif ($overwrite) {
479
+ foreach ($target as &$inner) {
480
+ $inner = $value;
481
+ }
482
+ }
483
+ } elseif (Arr::accessible($target)) {
484
+ if ($segments) {
485
+ if (! Arr::exists($target, $segment)) {
486
+ $target[$segment] = [];
487
+ }
488
+
489
+ data_set($target[$segment], $segments, $value, $overwrite);
490
+ } elseif ($overwrite || ! Arr::exists($target, $segment)) {
491
+ $target[$segment] = $value;
492
+ }
493
+ } elseif (is_object($target)) {
494
+ if ($segments) {
495
+ if (! isset($target->{$segment})) {
496
+ $target->{$segment} = [];
497
+ }
498
+
499
+ data_set($target->{$segment}, $segments, $value, $overwrite);
500
+ } elseif ($overwrite || ! isset($target->{$segment})) {
501
+ $target->{$segment} = $value;
502
+ }
503
+ } else {
504
+ $target = [];
505
+
506
+ if ($segments) {
507
+ data_set($target[$segment], $segments, $value, $overwrite);
508
+ } elseif ($overwrite) {
509
+ $target[$segment] = $value;
510
+ }
511
+ }
512
+
513
+ return $target;
514
+ }
515
+ }
516
+
517
+ if (! function_exists('dd')) {
518
+ /**
519
+ * Dump the passed variables and end the script.
520
+ *
521
+ * @param mixed
522
+ * @return void
523
+ */
524
+ function dd(...$args)
525
+ {
526
+ foreach ($args as $x) {
527
+ (new Dumper)->dump($x);
528
+ }
529
+
530
+ die(1);
531
+ }
532
+ }
533
+
534
+ if (! function_exists('e')) {
535
+ /**
536
+ * Escape HTML special characters in a string.
537
+ *
538
+ * @param \Illuminate\Contracts\Support\Htmlable|string $value
539
+ * @return string
540
+ */
541
+ function e($value)
542
+ {
543
+ if ($value instanceof Htmlable) {
544
+ return $value->toHtml();
545
+ }
546
+
547
+ return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false);
548
+ }
549
+ }
550
+
551
+ if (! function_exists('ends_with')) {
552
+ /**
553
+ * Determine if a given string ends with a given substring.
554
+ *
555
+ * @param string $haystack
556
+ * @param string|array $needles
557
+ * @return bool
558
+ */
559
+ function ends_with($haystack, $needles)
560
+ {
561
+ return Str::endsWith($haystack, $needles);
562
+ }
563
+ }
564
+
565
+ if (! function_exists('env')) {
566
+ /**
567
+ * Gets the value of an environment variable.
568
+ *
569
+ * @param string $key
570
+ * @param mixed $default
571
+ * @return mixed
572
+ */
573
+ function env($key, $default = null)
574
+ {
575
+ $value = getenv($key);
576
+
577
+ if ($value === false) {
578
+ return value($default);
579
+ }
580
+
581
+ switch (strtolower($value)) {
582
+ case 'true':
583
+ case '(true)':
584
+ return true;
585
+ case 'false':
586
+ case '(false)':
587
+ return false;
588
+ case 'empty':
589
+ case '(empty)':
590
+ return '';
591
+ case 'null':
592
+ case '(null)':
593
+ return;
594
+ }
595
+
596
+ if (strlen($value) > 1 && Str::startsWith($value, '"') && Str::endsWith($value, '"')) {
597
+ return substr($value, 1, -1);
598
+ }
599
+
600
+ return $value;
601
+ }
602
+ }
603
+
604
+ if (! function_exists('head')) {
605
+ /**
606
+ * Get the first element of an array. Useful for method chaining.
607
+ *
608
+ * @param array $array
609
+ * @return mixed
610
+ */
611
+ function head($array)
612
+ {
613
+ return reset($array);
614
+ }
615
+ }
616
+
617
+ if (! function_exists('kebab_case')) {
618
+ /**
619
+ * Convert a string to kebab case.
620
+ *
621
+ * @param string $value
622
+ * @return string
623
+ */
624
+ function kebab_case($value)
625
+ {
626
+ return Str::kebab($value);
627
+ }
628
+ }
629
+
630
+ if (! function_exists('last')) {
631
+ /**
632
+ * Get the last element from an array.
633
+ *
634
+ * @param array $array
635
+ * @return mixed
636
+ */
637
+ function last($array)
638
+ {
639
+ return end($array);
640
+ }
641
+ }
642
+
643
+ if (! function_exists('object_get')) {
644
+ /**
645
+ * Get an item from an object using "dot" notation.
646
+ *
647
+ * @param object $object
648
+ * @param string $key
649
+ * @param mixed $default
650
+ * @return mixed
651
+ */
652
+ function object_get($object, $key, $default = null)
653
+ {
654
+ if (is_null($key) || trim($key) == '') {
655
+ return $object;
656
+ }
657
+
658
+ foreach (explode('.', $key) as $segment) {
659
+ if (! is_object($object) || ! isset($object->{$segment})) {
660
+ return value($default);
661
+ }
662
+
663
+ $object = $object->{$segment};
664
+ }
665
+
666
+ return $object;
667
+ }
668
+ }
669
+
670
+ if (! function_exists('preg_replace_array')) {
671
+ /**
672
+ * Replace a given pattern with each value in the array in sequentially.
673
+ *
674
+ * @param string $pattern
675
+ * @param array $replacements
676
+ * @param string $subject
677
+ * @return string
678
+ */
679
+ function preg_replace_array($pattern, array $replacements, $subject)
680
+ {
681
+ return preg_replace_callback($pattern, function () use (&$replacements) {
682
+ foreach ($replacements as $key => $value) {
683
+ return array_shift($replacements);
684
+ }
685
+ }, $subject);
686
+ }
687
+ }
688
+
689
+ if (! function_exists('retry')) {
690
+ /**
691
+ * Retry an operation a given number of times.
692
+ *
693
+ * @param int $times
694
+ * @param callable $callback
695
+ * @param int $sleep
696
+ * @return mixed
697
+ *
698
+ * @throws \Exception
699
+ */
700
+ function retry($times, callable $callback, $sleep = 0)
701
+ {
702
+ $times--;
703
+
704
+ beginning:
705
+ try {
706
+ return $callback();
707
+ } catch (Exception $e) {
708
+ if (! $times) {
709
+ throw $e;
710
+ }
711
+
712
+ $times--;
713
+
714
+ if ($sleep) {
715
+ usleep($sleep * 1000);
716
+ }
717
+
718
+ goto beginning;
719
+ }
720
+ }
721
+ }
722
+
723
+ if (! function_exists('snake_case')) {
724
+ /**
725
+ * Convert a string to snake case.
726
+ *
727
+ * @param string $value
728
+ * @param string $delimiter
729
+ * @return string
730
+ */
731
+ function snake_case($value, $delimiter = '_')
732
+ {
733
+ return Str::snake($value, $delimiter);
734
+ }
735
+ }
736
+
737
+ if (! function_exists('starts_with')) {
738
+ /**
739
+ * Determine if a given string starts with a given substring.
740
+ *
741
+ * @param string $haystack
742
+ * @param string|array $needles
743
+ * @return bool
744
+ */
745
+ function starts_with($haystack, $needles)
746
+ {
747
+ return Str::startsWith($haystack, $needles);
748
+ }
749
+ }
750
+
751
+ if (! function_exists('str_after')) {
752
+ /**
753
+ * Return the remainder of a string after a given value.
754
+ *
755
+ * @param string $subject
756
+ * @param string $search
757
+ * @return string
758
+ */
759
+ function str_after($subject, $search)
760
+ {
761
+ return Str::after($subject, $search);
762
+ }
763
+ }
764
+
765
+ if (! function_exists('str_contains')) {
766
+ /**
767
+ * Determine if a given string contains a given substring.
768
+ *
769
+ * @param string $haystack
770
+ * @param string|array $needles
771
+ * @return bool
772
+ */
773
+ function str_contains($haystack, $needles)
774
+ {
775
+ return Str::contains($haystack, $needles);
776
+ }
777
+ }
778
+
779
+ if (! function_exists('str_finish')) {
780
+ /**
781
+ * Cap a string with a single instance of a given value.
782
+ *
783
+ * @param string $value
784
+ * @param string $cap
785
+ * @return string
786
+ */
787
+ function str_finish($value, $cap)
788
+ {
789
+ return Str::finish($value, $cap);
790
+ }
791
+ }
792
+
793
+ if (! function_exists('str_is')) {
794
+ /**
795
+ * Determine if a given string matches a given pattern.
796
+ *
797
+ * @param string $pattern
798
+ * @param string $value
799
+ * @return bool
800
+ */
801
+ function str_is($pattern, $value)
802
+ {
803
+ return Str::is($pattern, $value);
804
+ }
805
+ }
806
+
807
+ if (! function_exists('str_limit')) {
808
+ /**
809
+ * Limit the number of characters in a string.
810
+ *
811
+ * @param string $value
812
+ * @param int $limit
813
+ * @param string $end
814
+ * @return string
815
+ */
816
+ function str_limit($value, $limit = 100, $end = '...')
817
+ {
818
+ return Str::limit($value, $limit, $end);
819
+ }
820
+ }
821
+
822
+ if (! function_exists('str_plural')) {
823
+ /**
824
+ * Get the plural form of an English word.
825
+ *
826
+ * @param string $value
827
+ * @param int $count
828
+ * @return string
829
+ */
830
+ function str_plural($value, $count = 2)
831
+ {
832
+ return Str::plural($value, $count);
833
+ }
834
+ }
835
+
836
+ if (! function_exists('str_random')) {
837
+ /**
838
+ * Generate a more truly "random" alpha-numeric string.
839
+ *
840
+ * @param int $length
841
+ * @return string
842
+ *
843
+ * @throws \RuntimeException
844
+ */
845
+ function str_random($length = 16)
846
+ {
847
+ return Str::random($length);
848
+ }
849
+ }
850
+
851
+ if (! function_exists('str_replace_array')) {
852
+ /**
853
+ * Replace a given value in the string sequentially with an array.
854
+ *
855
+ * @param string $search
856
+ * @param array $replace
857
+ * @param string $subject
858
+ * @return string
859
+ */
860
+ function str_replace_array($search, array $replace, $subject)
861
+ {
862
+ return Str::replaceArray($search, $replace, $subject);
863
+ }
864
+ }
865
+
866
+ if (! function_exists('str_replace_first')) {
867
+ /**
868
+ * Replace the first occurrence of a given value in the string.
869
+ *
870
+ * @param string $search
871
+ * @param string $replace
872
+ * @param string $subject
873
+ * @return string
874
+ */
875
+ function str_replace_first($search, $replace, $subject)
876
+ {
877
+ return Str::replaceFirst($search, $replace, $subject);
878
+ }
879
+ }
880
+
881
+ if (! function_exists('str_replace_last')) {
882
+ /**
883
+ * Replace the last occurrence of a given value in the string.
884
+ *
885
+ * @param string $search
886
+ * @param string $replace
887
+ * @param string $subject
888
+ * @return string
889
+ */
890
+ function str_replace_last($search, $replace, $subject)
891
+ {
892
+ return Str::replaceLast($search, $replace, $subject);
893
+ }
894
+ }
895
+
896
+ if (! function_exists('str_singular')) {
897
+ /**
898
+ * Get the singular form of an English word.
899
+ *
900
+ * @param string $value
901
+ * @return string
902
+ */
903
+ function str_singular($value)
904
+ {
905
+ return Str::singular($value);
906
+ }
907
+ }
908
+
909
+ if (! function_exists('str_slug')) {
910
+ /**
911
+ * Generate a URL friendly "slug" from a given string.
912
+ *
913
+ * @param string $title
914
+ * @param string $separator
915
+ * @return string
916
+ */
917
+ function str_slug($title, $separator = '-')
918
+ {
919
+ return Str::slug($title, $separator);
920
+ }
921
+ }
922
+
923
+ if (! function_exists('str_start')) {
924
+ /**
925
+ * Begin a string with a single instance of a given value.
926
+ *
927
+ * @param string $value
928
+ * @param string $prefix
929
+ * @return string
930
+ */
931
+ function str_start($value, $prefix)
932
+ {
933
+ return Str::start($value, $prefix);
934
+ }
935
+ }
936
+
937
+ if (! function_exists('studly_case')) {
938
+ /**
939
+ * Convert a value to studly caps case.
940
+ *
941
+ * @param string $value
942
+ * @return string
943
+ */
944
+ function studly_case($value)
945
+ {
946
+ return Str::studly($value);
947
+ }
948
+ }
949
+
950
+ if (! function_exists('tap')) {
951
+ /**
952
+ * Call the given Closure with the given value then return the value.
953
+ *
954
+ * @param mixed $value
955
+ * @param callable|null $callback
956
+ * @return mixed
957
+ */
958
+ function tap($value, $callback = null)
959
+ {
960
+ if (is_null($callback)) {
961
+ return new HigherOrderTapProxy($value);
962
+ }
963
+
964
+ $callback($value);
965
+
966
+ return $value;
967
+ }
968
+ }
969
+
970
+ if (! function_exists('title_case')) {
971
+ /**
972
+ * Convert a value to title case.
973
+ *
974
+ * @param string $value
975
+ * @return string
976
+ */
977
+ function title_case($value)
978
+ {
979
+ return Str::title($value);
980
+ }
981
+ }
982
+
983
+ if (! function_exists('trait_uses_recursive')) {
984
+ /**
985
+ * Returns all traits used by a trait and its traits.
986
+ *
987
+ * @param string $trait
988
+ * @return array
989
+ */
990
+ function trait_uses_recursive($trait)
991
+ {
992
+ $traits = class_uses($trait);
993
+
994
+ foreach ($traits as $trait) {
995
+ $traits += trait_uses_recursive($trait);
996
+ }
997
+
998
+ return $traits;
999
+ }
1000
+ }
1001
+
1002
+ if (! function_exists('value')) {
1003
+ /**
1004
+ * Return the default value of the given value.
1005
+ *
1006
+ * @param mixed $value
1007
+ * @return mixed
1008
+ */
1009
+ function value($value)
1010
+ {
1011
+ return $value instanceof Closure ? $value() : $value;
1012
+ }
1013
+ }
1014
+
1015
+ if (! function_exists('windows_os')) {
1016
+ /**
1017
+ * Determine whether the current environment is Windows based.
1018
+ *
1019
+ * @return bool
1020
+ */
1021
+ function windows_os()
1022
+ {
1023
+ return strtolower(substr(PHP_OS, 0, 3)) === 'win';
1024
+ }
1025
+ }
1026
+
1027
+ if (! function_exists('with')) {
1028
+ /**
1029
+ * Return the given object. Useful for chaining.
1030
+ *
1031
+ * @param mixed $object
1032
+ * @return mixed
1033
+ */
1034
+ function with($object)
1035
+ {
1036
+ return $object;
1037
+ }
1038
+ }
vendor/paragonie/random_compat/LICENSE ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Paragon Initiative Enterprises
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
vendor/paragonie/random_compat/build-phar.sh ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+
3
+ basedir=$( dirname $( readlink -f ${BASH_SOURCE[0]} ) )
4
+
5
+ php -dphar.readonly=0 "$basedir/other/build_phar.php" $*
vendor/paragonie/random_compat/composer.json ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "paragonie/random_compat",
3
+ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
4
+ "keywords": [
5
+ "csprng",
6
+ "random",
7
+ "pseudorandom"
8
+ ],
9
+ "license": "MIT",
10
+ "type": "library",
11
+ "authors": [
12
+ {
13
+ "name": "Paragon Initiative Enterprises",
14
+ "email": "security@paragonie.com",
15
+ "homepage": "https://paragonie.com"
16
+ }
17
+ ],
18
+ "support": {
19
+ "issues": "https://github.com/paragonie/random_compat/issues",
20
+ "email": "info@paragonie.com",
21
+ "source": "https://github.com/paragonie/random_compat"
22
+ },
23
+ "require": {
24
+ "php": ">=5.2.0"
25
+ },
26
+ "require-dev": {
27
+ "phpunit/phpunit": "4.*|5.*"
28
+ },
29
+ "suggest": {
30
+ "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
31
+ },
32
+ "autoload": {
33
+ "files": [
34
+ "lib/random.php"
35
+ ]
36
+ }
37
+ }
vendor/paragonie/random_compat/dist/random_compat.phar.pubkey ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ -----BEGIN PUBLIC KEY-----
2
+ MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEd+wCqJDrx5B4OldM0dQE0ZMX+lx1ZWm
3
+ pui0SUqD4G29L3NGsz9UhJ/0HjBdbnkhIK5xviT0X5vtjacF6ajgcCArbTB+ds+p
4
+ +h7Q084NuSuIpNb6YPfoUFgC/CL9kAoc
5
+ -----END PUBLIC KEY-----
vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN PGP SIGNATURE-----
2
+ Version: GnuPG v2.0.22 (MingW32)
3
+
4
+ iQEcBAABAgAGBQJWtW1hAAoJEGuXocKCZATaJf0H+wbZGgskK1dcRTsuVJl9IWip
5
+ QwGw/qIKI280SD6/ckoUMxKDCJiFuPR14zmqnS36k7N5UNPnpdTJTS8T11jttSpg
6
+ 1LCmgpbEIpgaTah+cELDqFCav99fS+bEiAL5lWDAHBTE/XPjGVCqeehyPYref4IW
7
+ NDBIEsvnHPHPLsn6X5jq4+Yj5oUixgxaMPiR+bcO4Sh+RzOVB6i2D0upWfRXBFXA
8
+ NNnsg9/zjvoC7ZW73y9uSH+dPJTt/Vgfeiv52/v41XliyzbUyLalf02GNPY+9goV
9
+ JHG1ulEEBJOCiUD9cE1PUIJwHA/HqyhHIvV350YoEFiHl8iSwm7SiZu5kPjaq74=
10
+ =B6+8
11
+ -----END PGP SIGNATURE-----
vendor/paragonie/random_compat/lib/byte_safe_strings.php ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Random_* Compatibility Library
4
+ * for using the new PHP 7 random_* API in PHP 5 projects
5
+ *
6
+ * The MIT License (MIT)
7
+ *
8
+ * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
9
+ *
10
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ * of this software and associated documentation files (the "Software"), to deal
12
+ * in the Software without restriction, including without limitation the rights
13
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ * copies of the Software, and to permit persons to whom the Software is
15
+ * furnished to do so, subject to the following conditions:
16
+ *
17
+ * The above copyright notice and this permission notice shall be included in
18
+ * all copies or substantial portions of the Software.
19
+ *
20
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ * SOFTWARE.
27
+ */
28
+
29
+ if (!is_callable('RandomCompat_strlen')) {
30
+ if (
31
+ defined('MB_OVERLOAD_STRING') &&
32
+ ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING
33
+ ) {
34
+ /**
35
+ * strlen() implementation that isn't brittle to mbstring.func_overload
36
+ *
37
+ * This version uses mb_strlen() in '8bit' mode to treat strings as raw
38
+ * binary rather than UTF-8, ISO-8859-1, etc
39
+ *
40
+ * @param string $binary_string
41
+ *
42
+ * @throws TypeError
43
+ *
44
+ * @return int
45
+ */
46
+ function RandomCompat_strlen($binary_string)
47
+ {
48
+ if (!is_string($binary_string)) {
49
+ throw new TypeError(
50
+ 'RandomCompat_strlen() expects a string'
51
+ );
52
+ }
53
+
54
+ return (int) mb_strlen($binary_string, '8bit');
55
+ }
56
+
57
+ } else {
58
+ /**
59
+ * strlen() implementation that isn't brittle to mbstring.func_overload
60
+ *
61
+ * This version just used the default strlen()
62
+ *
63
+ * @param string $binary_string
64
+ *
65
+ * @throws TypeError
66
+ *
67
+ * @return int
68
+ */
69
+ function RandomCompat_strlen($binary_string)
70
+ {
71
+ if (!is_string($binary_string)) {
72
+ throw new TypeError(
73
+ 'RandomCompat_strlen() expects a string'
74
+ );
75
+ }
76
+ return (int) strlen($binary_string);
77
+ }
78
+ }
79
+ }
80
+
81
+ if (!is_callable('RandomCompat_substr')) {
82
+
83
+ if (
84
+ defined('MB_OVERLOAD_STRING')
85
+ &&
86
+ ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING
87
+ ) {
88
+ /**
89
+ * substr() implementation that isn't brittle to mbstring.func_overload
90
+ *
91
+ * This version uses mb_substr() in '8bit' mode to treat strings as raw
92
+ * binary rather than UTF-8, ISO-8859-1, etc
93
+ *
94
+ * @param string $binary_string
95
+ * @param int $start
96
+ * @param int $length (optional)
97
+ *
98
+ * @throws TypeError
99
+ *
100
+ * @return string
101
+ */
102
+ function RandomCompat_substr($binary_string, $start, $length = null)
103
+ {
104
+ if (!is_string($binary_string)) {
105
+ throw new TypeError(
106
+ 'RandomCompat_substr(): First argument should be a string'
107
+ );
108
+ }
109
+
110
+ if (!is_int($start)) {
111
+ throw new TypeError(
112
+ 'RandomCompat_substr(): Second argument should be an integer'
113
+ );
114
+ }
115
+
116
+ if ($length === null) {
117
+ /**
118
+ * mb_substr($str, 0, NULL, '8bit') returns an empty string on
119
+ * PHP 5.3, so we have to find the length ourselves.
120
+ */
121
+ $length = RandomCompat_strlen($binary_string) - $start;
122
+ } elseif (!is_int($length)) {
123
+ throw new TypeError(
124
+ 'RandomCompat_substr(): Third argument should be an integer, or omitted'
125
+ );
126
+ }
127
+
128
+ // Consistency with PHP's behavior
129
+ if ($start === RandomCompat_strlen($binary_string) && $length === 0) {
130
+ return '';
131
+ }
132
+ if ($start > RandomCompat_strlen($binary_string)) {
133
+ return '';
134
+ }
135
+
136
+ return (string) mb_substr($binary_string, $start, $length, '8bit');
137
+ }
138
+
139
+ } else {
140
+
141
+ /**
142
+ * substr() implementation that isn't brittle to mbstring.func_overload
143
+ *
144
+ * This version just uses the default substr()
145
+ *
146
+ * @param string $binary_string
147
+ * @param int $start
148
+ * @param int $length (optional)
149
+ *
150
+ * @throws TypeError
151
+ *
152
+ * @return string
153
+ */
154
+ function RandomCompat_substr($binary_string, $start, $length = null)
155
+ {
156
+ if (!is_string($binary_string)) {
157
+ throw new TypeError(
158
+ 'RandomCompat_substr(): First argument should be a string'
159
+ );
160
+ }
161
+
162
+ if (!is_int($start)) {
163
+ throw new TypeError(
164
+ 'RandomCompat_substr(): Second argument should be an integer'
165
+ );
166
+ }
167
+
168
+ if ($length !== null) {
169
+ if (!is_int($length)) {
170
+ throw new TypeError(
171
+ 'RandomCompat_substr(): Third argument should be an integer, or omitted'
172
+ );
173
+ }
174
+
175
+ return (string) substr($binary_string, $start, $length);
176
+ }
177
+
178
+ return (string) substr($binary_string, $start);
179
+ }
180
+ }
181
+ }
vendor/paragonie/random_compat/lib/cast_to_int.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Random_* Compatibility Library
4
+ * for using the new PHP 7 random_* API in PHP 5 projects
5
+ *
6
+ * The MIT License (MIT)
7
+ *
8
+ * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
9
+ *
10
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ * of this software and associated documentation files (the "Software"), to deal
12
+ * in the Software without restriction, including without limitation the rights
13
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ * copies of the Software, and to permit persons to whom the Software is
15
+ * furnished to do so, subject to the following conditions:
16
+ *
17
+ * The above copyright notice and this permission notice shall be included in
18
+ * all copies or substantial portions of the Software.
19
+ *
20
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ * SOFTWARE.
27
+ */
28
+
29
+ if (!is_callable('RandomCompat_intval')) {
30
+
31
+ /**
32
+ * Cast to an integer if we can, safely.
33
+ *
34
+ * If you pass it a float in the range (~PHP_INT_MAX, PHP_INT_MAX)
35
+ * (non-inclusive), it will sanely cast it to an int. If you it's equal to
36
+ * ~PHP_INT_MAX or PHP_INT_MAX, we let it fail as not an integer. Floats
37
+ * lose precision, so the <= and => operators might accidentally let a float
38
+ * through.
39
+ *
40
+ * @param int|float $number The number we want to convert to an int
41
+ * @param bool $fail_open Set to true to not throw an exception
42
+ *
43
+ * @return float|int
44
+ * @psalm-suppress InvalidReturnType
45
+ *
46
+ * @throws TypeError
47
+ */
48
+ function RandomCompat_intval($number, $fail_open = false)
49
+ {
50
+ if (is_int($number) || is_float($number)) {
51
+ $number += 0;
52
+ } elseif (is_numeric($number)) {
53
+ $number += 0;
54
+ }
55
+
56
+ if (
57
+ is_float($number)
58
+ &&
59
+ $number > ~PHP_INT_MAX
60
+ &&
61
+ $number < PHP_INT_MAX
62
+ ) {
63
+ $number = (int) $number;
64
+ }
65
+
66
+ if (is_int($number)) {
67
+ return (int) $number;
68
+ } elseif (!$fail_open) {
69
+ throw new TypeError(
70
+ 'Expected an integer.'
71
+ );
72
+ }
73
+ return $number;
74
+ }
75
+ }
vendor/paragonie/random_compat/lib/error_polyfill.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Random_* Compatibility Library
4
+ * for using the new PHP 7 random_* API in PHP 5 projects
5
+ *
6
+ * The MIT License (MIT)
7
+ *
8
+ * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
9
+ *
10
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ * of this software and associated documentation files (the "Software"), to deal
12
+ * in the Software without restriction, including without limitation the rights
13
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ * copies of the Software, and to permit persons to whom the Software is
15
+ * furnished to do so, subject to the following conditions:
16
+ *
17
+ * The above copyright notice and this permission notice shall be included in
18
+ * all copies or substantial portions of the Software.
19
+ *
20
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ * SOFTWARE.
27
+ */
28
+
29
+ if (!class_exists('Error', false)) {
30
+ // We can't really avoid making this extend Exception in PHP 5.
31
+ class Error extends Exception
32
+ {
33
+
34
+ }
35
+ }
36
+
37
+ if (!class_exists('TypeError', false)) {
38
+ if (is_subclass_of('Error', 'Exception')) {
39
+ class TypeError extends Error
40
+ {
41
+
42
+ }
43
+ } else {
44
+ class TypeError extends Exception
45
+ {
46
+
47
+ }
48
+ }
49
+ }
vendor/paragonie/random_compat/lib/random.php ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Random_* Compatibility Library
4
+ * for using the new PHP 7 random_* API in PHP 5 projects
5
+ *
6
+ * @version 2.0.10
7
+ * @released 2017-03-13
8
+ *
9
+ * The MIT License (MIT)
10
+ *
11
+ * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
12
+ *
13
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ * of this software and associated documentation files (the "Software"), to deal
15
+ * in the Software without restriction, including without limitation the rights
16
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ * copies of the Software, and to permit persons to whom the Software is
18
+ * furnished to do so, subject to the following conditions:
19
+ *
20
+ * The above copyright notice and this permission notice shall be included in
21
+ * all copies or substantial portions of the Software.
22
+ *
23
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ * SOFTWARE.
30
+ */
31
+
32
+ if (!defined('PHP_VERSION_ID')) {
33
+ // This constant was introduced in PHP 5.2.7
34
+ $RandomCompatversion = array_map('intval', explode('.', PHP_VERSION));
35
+ define(
36
+ 'PHP_VERSION_ID',
37
+ $RandomCompatversion[0] * 10000
38
+ + $RandomCompatversion[1] * 100
39
+ + $RandomCompatversion[2]
40
+ );
41
+ $RandomCompatversion = null;
42
+ }
43
+
44
+ /**
45
+ * PHP 7.0.0 and newer have these functions natively.
46
+ */
47
+ if (PHP_VERSION_ID >= 70000) {
48
+ return;
49
+ }
50
+
51
+ if (!defined('RANDOM_COMPAT_READ_BUFFER')) {
52
+ define('RANDOM_COMPAT_READ_BUFFER', 8);
53
+ }
54
+
55
+ $RandomCompatDIR = dirname(__FILE__);
56
+
57
+ require_once $RandomCompatDIR . '/byte_safe_strings.php';
58
+ require_once $RandomCompatDIR . '/cast_to_int.php';
59
+ require_once $RandomCompatDIR . '/error_polyfill.php';
60
+
61
+ if (!is_callable('random_bytes')) {
62
+ /**
63
+ * PHP 5.2.0 - 5.6.x way to implement random_bytes()
64
+ *
65
+ * We use conditional statements here to define the function in accordance
66
+ * to the operating environment. It's a micro-optimization.
67
+ *
68
+ * In order of preference:
69
+ * 1. Use libsodium if available.
70
+ * 2. fread() /dev/urandom if available (never on Windows)
71
+ * 3. mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM)
72
+ * 4. COM('CAPICOM.Utilities.1')->GetRandom()
73
+ *
74
+ * See RATIONALE.md for our reasoning behind this particular order
75
+ */
76
+ if (extension_loaded('libsodium')) {
77
+ // See random_bytes_libsodium.php
78
+ if (PHP_VERSION_ID >= 50300 && is_callable('\\Sodium\\randombytes_buf')) {
79
+ require_once $RandomCompatDIR . '/random_bytes_libsodium.php';
80
+ } elseif (method_exists('Sodium', 'randombytes_buf')) {
81
+ require_once $RandomCompatDIR . '/random_bytes_libsodium_legacy.php';
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Reading directly from /dev/urandom:
87
+ */
88
+ if (DIRECTORY_SEPARATOR === '/') {
89
+ // DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast
90
+ // way to exclude Windows.
91
+ $RandomCompatUrandom = true;
92
+ $RandomCompat_basedir = ini_get('open_basedir');
93
+
94
+ if (!empty($RandomCompat_basedir)) {
95
+ $RandomCompat_open_basedir = explode(
96
+ PATH_SEPARATOR,
97
+ strtolower($RandomCompat_basedir)
98
+ );
99
+ $RandomCompatUrandom = (array() !== array_intersect(
100
+ array('/dev', '/dev/', '/dev/urandom'),
101
+ $RandomCompat_open_basedir
102
+ ));
103
+ $RandomCompat_open_basedir = null;
104
+ }
105
+
106
+ if (
107
+ !is_callable('random_bytes')
108
+ &&
109
+ $RandomCompatUrandom
110
+ &&
111
+ @is_readable('/dev/urandom')
112
+ ) {
113
+ // Error suppression on is_readable() in case of an open_basedir
114
+ // or safe_mode failure. All we care about is whether or not we
115
+ // can read it at this point. If the PHP environment is going to
116
+ // panic over trying to see if the file can be read in the first
117
+ // place, that is not helpful to us here.
118
+
119
+ // See random_bytes_dev_urandom.php
120
+ require_once $RandomCompatDIR . '/random_bytes_dev_urandom.php';
121
+ }
122
+ // Unset variables after use
123
+ $RandomCompat_basedir = null;
124
+ } else {
125
+ $RandomCompatUrandom = false;
126
+ }
127
+
128
+ /**
129
+ * mcrypt_create_iv()
130
+ *
131
+ * We only want to use mcypt_create_iv() if:
132
+ *
133
+ * - random_bytes() hasn't already been defined
134
+ * - the mcrypt extensions is loaded
135
+ * - One of these two conditions is true:
136
+ * - We're on Windows (DIRECTORY_SEPARATOR !== '/')
137
+ * - We're not on Windows and /dev/urandom is readabale
138
+ * (i.e. we're not in a chroot jail)
139
+ * - Special case:
140
+ * - If we're not on Windows, but the PHP version is between
141
+ * 5.6.10 and 5.6.12, we don't want to use mcrypt. It will
142
+ * hang indefinitely. This is bad.
143
+ * - If we're on Windows, we want to use PHP >= 5.3.7 or else
144
+ * we get insufficient entropy errors.
145
+ */
146
+ if (
147
+ !is_callable('random_bytes')
148
+ &&
149
+ // Windows on PHP < 5.3.7 is broken, but non-Windows is not known to be.
150
+ (DIRECTORY_SEPARATOR === '/' || PHP_VERSION_ID >= 50307)
151
+ &&
152
+ // Prevent this code from hanging indefinitely on non-Windows;
153
+ // see https://bugs.php.net/bug.php?id=69833
154
+ (
155
+ DIRECTORY_SEPARATOR !== '/' ||
156
+ (PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613)
157
+ )
158
+ &&
159
+ extension_loaded('mcrypt')
160
+ ) {
161
+ // See random_bytes_mcrypt.php
162
+ require_once $RandomCompatDIR . '/random_bytes_mcrypt.php';
163
+ }
164
+ $RandomCompatUrandom = null;
165
+
166
+ /**
167
+ * This is a Windows-specific fallback, for when the mcrypt extension
168
+ * isn't loaded.
169
+ */
170
+ if (
171
+ !is_callable('random_bytes')
172
+ &&
173
+ extension_loaded('com_dotnet')
174
+ &&
175
+ class_exists('COM')
176
+ ) {
177
+ $RandomCompat_disabled_classes = preg_split(
178
+ '#\s*,\s*#',
179
+ strtolower(ini_get('disable_classes'))
180
+ );
181
+
182
+ if (!in_array('com', $RandomCompat_disabled_classes)) {
183
+ try {
184
+ $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1');
185
+ if (method_exists($RandomCompatCOMtest, 'GetRandom')) {
186
+ // See random_bytes_com_dotnet.php
187
+ require_once $RandomCompatDIR . '/random_bytes_com_dotnet.php';
188
+ }
189
+ } catch (com_exception $e) {
190
+ // Don't try to use it.
191
+ }
192
+ }
193
+ $RandomCompat_disabled_classes = null;
194
+ $RandomCompatCOMtest = null;
195
+ }
196
+
197
+ /**
198
+ * throw new Exception
199
+ */
200
+ if (!is_callable('random_bytes')) {
201
+ /**
202
+ * We don't have any more options, so let's throw an exception right now
203
+ * and hope the developer won't let it fail silently.
204
+ *
205
+ * @param mixed $length
206
+ * @return void
207
+ * @throws Exception
208
+ */
209
+ function random_bytes($length)
210
+ {
211
+ unset($length); // Suppress "variable not used" warnings.
212
+ throw new Exception(
213
+ 'There is no suitable CSPRNG installed on your system'
214
+ );
215
+ }
216
+ }
217
+ }
218
+
219
+ if (!is_callable('random_int')) {
220
+ require_once $RandomCompatDIR . '/random_int.php';
221
+ }
222
+
223
+ $RandomCompatDIR = null;
vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Random_* Compatibility Library
4
+ * for using the new PHP 7 random_* API in PHP 5 projects
5
+ *
6
+ * The MIT License (MIT)
7
+ *
8
+ * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
9
+ *
10
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ * of this software and associated documentation files (the "Software"), to deal
12
+ * in the Software without restriction, including without limitation the rights
13
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ * copies of the Software, and to permit persons to whom the Software is
15
+ * furnished to do so, subject to the following conditions:
16
+ *
17
+ * The above copyright notice and this permission notice shall be included in
18
+ * all copies or substantial portions of the Software.
19
+ *
20
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ * SOFTWARE.
27
+ */
28
+
29
+ if (!is_callable('random_bytes')) {
30
+ /**
31
+ * Windows with PHP < 5.3.0 will not have the function
32
+ * openssl_random_pseudo_bytes() available, so let's use
33
+ * CAPICOM to work around this deficiency.
34
+ *
35
+ * @param int $bytes
36
+ *
37
+ * @throws Exception
38
+ *
39
+ * @return string
40
+ */
41
+ function random_bytes($bytes)
42
+ {
43
+ try {
44
+ $bytes = RandomCompat_intval($bytes);
45
+ } catch (TypeError $ex) {
46
+ throw new TypeError(
47
+ 'random_bytes(): $bytes must be an integer'
48
+ );
49
+ }
50
+
51
+ if ($bytes < 1) {
52
+ throw new Error(
53
+ 'Length must be greater than 0'
54
+ );
55
+ }
56
+
57
+ $buf = '';
58
+ if (!class_exists('COM')) {
59
+ throw new Error(
60
+ 'COM does not exist'
61
+ );
62
+ }
63
+ $util = new COM('CAPICOM.Utilities.1');
64
+ $execCount = 0;
65
+
66
+ /**
67
+ * Let's not let it loop forever. If we run N times and fail to
68
+ * get N bytes of random data, then CAPICOM has failed us.
69
+ */
70
+ do {
71
+ $buf .= base64_decode($util->GetRandom($bytes, 0));
72
+ if (RandomCompat_strlen($buf) >= $bytes) {
73
+ /**
74
+ * Return our random entropy buffer here:
75
+ */
76
+ return RandomCompat_substr($buf, 0, $bytes);
77
+ }
78
+ ++$execCount;
79
+ } while ($execCount < $bytes);
80
+
81
+ /**
82
+ * If we reach here, PHP has failed us.
83
+ */
84
+ throw new Exception(
85
+ 'Could not gather sufficient random data'
86
+ );
87
+ }
88
+ }
vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Random_* Compatibility Library
4
+ * for using the new PHP 7 random_* API in PHP 5 projects
5
+ *
6
+ * The MIT License (MIT)
7
+ *
8
+ * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
9
+ *
10
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ * of this software and associated documentation files (the "Software"), to deal
12
+ * in the Software without restriction, including without limitation the rights
13
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ * copies of the Software, and to permit persons to whom the Software is
15
+ * furnished to do so, subject to the following conditions:
16
+ *
17
+ * The above copyright notice and this permission notice shall be included in
18
+ * all copies or substantial portions of the Software.
19
+ *
20
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ * SOFTWARE.
27
+ */
28
+
29
+ if (!defined('RANDOM_COMPAT_READ_BUFFER')) {
30
+ define('RANDOM_COMPAT_READ_BUFFER', 8);
31
+ }
32
+
33
+ if (!is_callable('random_bytes')) {
34
+ /**
35
+ * Unless open_basedir is enabled, use /dev/urandom for
36
+ * random numbers in accordance with best practices
37
+ *
38
+ * Why we use /dev/urandom and not /dev/random
39
+ * @ref http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers
40
+ *
41
+ * @param int $bytes
42
+ *
43
+ * @throws Exception
44
+ *
45
+ * @return string
46
+ */
47
+ function random_bytes($bytes)
48
+ {
49
+ static $fp = null;
50
+ /**
51
+ * This block should only be run once
52
+ */
53
+ if (empty($fp)) {
54
+ /**
55
+ * We use /dev/urandom if it is a char device.
56
+ * We never fall back to /dev/random
57
+ */
58
+ $fp = fopen('/dev/urandom', 'rb');
59
+ if (!empty($fp)) {
60
+ $st = fstat($fp);
61
+ if (($st['mode'] & 0170000) !== 020000) {
62
+ fclose($fp);
63
+ $fp = false;
64
+ }
65
+ }
66
+
67
+ if (!empty($fp)) {
68
+ /**
69
+ * stream_set_read_buffer() does not exist in HHVM
70
+ *
71
+ * If we don't set the stream's read buffer to 0, PHP will
72
+ * internally buffer 8192 bytes, which can waste entropy
73
+ *
74
+ * stream_set_read_buffer returns 0 on success
75
+ */
76
+ if (is_callable('stream_set_read_buffer')) {
77
+ stream_set_read_buffer($fp, RANDOM_COMPAT_READ_BUFFER);
78
+ }
79
+ if (is_callable('stream_set_chunk_size')) {
80
+ stream_set_chunk_size($fp, RANDOM_COMPAT_READ_BUFFER);
81
+ }
82
+ }
83
+ }
84
+
85
+ try {
86
+ $bytes = RandomCompat_intval($bytes);
87
+ } catch (TypeError $ex) {
88
+ throw new TypeError(
89
+ 'random_bytes(): $bytes must be an integer'
90
+ );
91
+ }
92
+
93
+ if ($bytes < 1) {
94
+ throw new Error(
95
+ 'Length must be greater than 0'
96
+ );
97
+ }
98
+
99
+ /**
100
+ * This if() block only runs if we managed to open a file handle
101
+ *
102
+ * It does not belong in an else {} block, because the above
103
+ * if (empty($fp)) line is logic that should only be run once per
104
+ * page load.
105
+ */
106
+ if (!empty($fp)) {
107
+ /**
108
+ * @var int
109
+ */
110
+ $remaining = $bytes;
111
+
112
+ /**
113
+ * @var string|bool
114
+ */
115
+ $buf = '';
116
+
117
+ /**
118
+ * We use fread() in a loop to protect against partial reads
119
+ */
120
+ do {
121
+ /**
122
+ * @var string|bool
123
+ */
124
+ $read = fread($fp, $remaining);
125
+ if (!is_string($read)) {
126
+ if ($read === false) {
127
+ /**
128
+ * We cannot safely read from the file. Exit the
129
+ * do-while loop and trigger the exception condition
130
+ *
131
+ * @var string|bool
132
+ */
133
+ $buf = false;
134
+ break;
135
+ }
136
+ }
137
+ /**
138
+ * Decrease the number of bytes returned from remaining
139
+ */
140
+ $remaining -= RandomCompat_strlen($read);
141
+ /**
142
+ * @var string|bool
143
+ */
144
+ $buf = $buf . $read;
145
+ } while ($remaining > 0);
146
+
147
+ /**
148
+ * Is our result valid?
149
+ */
150
+ if (is_string($buf)) {
151
+ if (RandomCompat_strlen($buf) === $bytes) {
152
+ /**
153
+ * Return our random entropy buffer here:
154
+ */
155
+ return $buf;
156
+ }
157
+ }
158
+ }
159
+
160
+ /**
161
+ * If we reach here, PHP has failed us.
162
+ */
163
+ throw new Exception(
164
+ 'Error reading from source device'
165
+ );
166
+ }
167
+ }
vendor/paragonie/random_compat/lib/random_bytes_libsodium.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Random_* Compatibility Library
4
+ * for using the new PHP 7 random_* API in PHP 5 projects
5
+ *
6
+ * The MIT License (MIT)
7
+ *
8
+ * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
9
+ *
10
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ * of this software and associated documentation files (the "Software"), to deal
12
+ * in the Software without restriction, including without limitation the rights
13
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ * copies of the Software, and to permit persons to whom the Software is
15
+ * furnished to do so, subject to the following conditions:
16
+ *
17
+ * The above copyright notice and this permission notice shall be included in
18
+ * all copies or substantial portions of the Software.
19
+ *
20
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ * SOFTWARE.
27
+ */
28
+
29
+ if (!is_callable('random_bytes')) {
30
+ /**
31
+ * If the libsodium PHP extension is loaded, we'll use it above any other
32
+ * solution.
33
+ *
34
+ * libsodium-php project:
35
+ * @ref https://github.com/jedisct1/libsodium-php
36
+ *
37
+ * @param int $bytes
38
+ *
39
+ * @throws Exception
40
+ *
41
+ * @return string
42
+ */
43
+ function random_bytes($bytes)
44
+ {
45
+ try {
46
+ $bytes = RandomCompat_intval($bytes);
47
+ } catch (TypeError $ex) {
48
+ throw new TypeError(
49
+ 'random_bytes(): $bytes must be an integer'
50
+ );
51
+ }
52
+
53
+ if ($bytes < 1) {
54
+ throw new Error(
55
+ 'Length must be greater than 0'
56
+ );
57
+ }
58
+
59
+ /**
60
+ * \Sodium\randombytes_buf() doesn't allow more than 2147483647 bytes to be
61
+ * generated in one invocation.
62
+ */
63
+ if ($bytes > 2147483647) {
64
+ $buf = '';
65
+ for ($i = 0; $i < $bytes; $i += 1073741824) {
66
+ $n = ($bytes - $i) > 1073741824
67
+ ? 1073741824
68
+ : $bytes - $i;
69
+ $buf .= \Sodium\randombytes_buf($n);
70
+ }
71
+ } else {
72
+ $buf = \Sodium\randombytes_buf($bytes);
73
+ }
74
+
75
+ if ($buf !== false) {
76
+ if (RandomCompat_strlen($buf) === $bytes) {
77
+ return $buf;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * If we reach here, PHP has failed us.
83
+ */
84
+ throw new Exception(
85
+ 'Could not gather sufficient random data'
86
+ );
87
+ }
88
+ }
vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Random_* Compatibility Library
4
+ * for using the new PHP 7 random_* API in PHP 5 projects
5
+ *
6
+ * The MIT License (MIT)
7
+ *
8
+ * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
9
+ *
10
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ * of this software and associated documentation files (the "Software"), to deal
12
+ * in the Software without restriction, including without limitation the rights
13
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ * copies of the Software, and to permit persons to whom the Software is
15
+ * furnished to do so, subject to the following conditions:
16
+ *
17
+ * The above copyright notice and this permission notice shall be included in
18
+ * all copies or substantial portions of the Software.
19
+ *
20
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ * SOFTWARE.
27
+ */
28
+
29
+ if (!is_callable('random_bytes')) {
30
+ /**
31
+ * If the libsodium PHP extension is loaded, we'll use it above any other
32
+ * solution.
33
+ *
34
+ * libsodium-php project:
35
+ * @ref https://github.com/jedisct1/libsodium-php
36
+ *
37
+ * @param int $bytes
38
+ *
39
+ * @throws Exception
40
+ *
41
+ * @return string
42
+ */
43
+ function random_bytes($bytes)
44
+ {
45
+ try {
46
+ $bytes = RandomCompat_intval($bytes);
47
+ } catch (TypeError $ex) {
48
+ throw new TypeError(
49
+ 'random_bytes(): $bytes must be an integer'
50
+ );
51
+ }
52
+
53
+ if ($bytes < 1) {
54
+ throw new Error(
55
+ 'Length must be greater than 0'
56
+ );
57
+ }
58
+
59
+ /**
60
+ * @var string
61
+ */
62
+ $buf = '';
63
+
64
+ /**
65
+ * \Sodium\randombytes_buf() doesn't allow more than 2147483647 bytes to be
66
+ * generated in one invocation.
67
+ */
68
+ if ($bytes > 2147483647) {
69
+ for ($i = 0; $i < $bytes; $i += 1073741824) {
70
+ $n = ($bytes - $i) > 1073741824
71
+ ? 1073741824
72
+ : $bytes - $i;
73
+ $buf .= Sodium::randombytes_buf((int) $n);
74
+ }
75
+ } else {
76
+ $buf .= Sodium::randombytes_buf((int) $bytes);
77
+ }
78
+
79
+ if (is_string($buf)) {
80
+ if (RandomCompat_strlen($buf) === $bytes) {
81
+ return $buf;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * If we reach here, PHP has failed us.
87
+ */
88
+ throw new Exception(
89
+ 'Could not gather sufficient random data'
90
+ );
91
+ }
92
+ }
vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Random_* Compatibility Library
4
+ * for using the new PHP 7 random_* API in PHP 5 projects
5
+ *
6
+ * The MIT License (MIT)
7
+ *
8
+ * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
9
+ *
10
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ * of this software and associated documentation files (the "Software"), to deal
12
+ * in the Software without restriction, including without limitation the rights
13
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ * copies of the Software, and to permit persons to whom the Software is
15
+ * furnished to do so, subject to the following conditions:
16
+ *
17
+ * The above copyright notice and this permission notice shall be included in
18
+ * all copies or substantial portions of the Software.
19
+ *
20
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ * SOFTWARE.
27
+ */
28
+
29
+ if (!is_callable('random_bytes')) {
30
+ /**
31
+ * Powered by ext/mcrypt (and thankfully NOT libmcrypt)
32
+ *
33
+ * @ref https://bugs.php.net/bug.php?id=55169
34
+ * @ref https://github.com/php/php-src/blob/c568ffe5171d942161fc8dda066bce844bdef676/ext/mcrypt/mcrypt.c#L1321-L1386
35
+ *
36
+ * @param int $bytes
37
+ *
38
+ * @throws Exception
39
+ *
40
+ * @return string
41
+ */
42
+ function random_bytes($bytes)
43
+ {
44
+ try {
45
+ $bytes = RandomCompat_intval($bytes);
46
+ } catch (TypeError $ex) {
47
+ throw new TypeError(
48
+ 'random_bytes(): $bytes must be an integer'
49
+ );
50
+ }
51
+
52
+ if ($bytes < 1) {
53
+ throw new Error(
54
+ 'Length must be greater than 0'
55
+ );
56
+ }
57
+
58
+ $buf = @mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM);
59
+ if (
60
+ $buf !== false
61
+ &&
62
+ RandomCompat_strlen($buf) === $bytes
63
+ ) {
64
+ /**
65
+ * Return our random entropy buffer here:
66
+ */
67
+ return $buf;
68
+ }
69
+
70
+ /**
71
+ * If we reach here, PHP has failed us.
72
+ */
73
+ throw new Exception(
74
+ 'Could not gather sufficient random data'
75
+ );
76
+ }
77
+ }
vendor/paragonie/random_compat/lib/random_int.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if (!is_callable('random_int')) {
4
+ /**
5
+ * Random_* Compatibility Library
6
+ * for using the new PHP 7 random_* API in PHP 5 projects
7
+ *
8
+ * The MIT License (MIT)
9
+ *
10
+ * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
11
+ *
12
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ * of this software and associated documentation files (the "Software"), to deal
14
+ * in the Software without restriction, including without limitation the rights
15
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ * copies of the Software, and to permit persons to whom the Software is
17
+ * furnished to do so, subject to the following conditions:
18
+ *
19
+ * The above copyright notice and this permission notice shall be included in
20
+ * all copies or substantial portions of the Software.
21
+ *
22
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ * SOFTWARE.
29
+ */
30
+
31
+ /**
32
+ * Fetch a random integer between $min and $max inclusive
33
+ *
34
+ * @param int $min
35
+ * @param int $max
36
+ *
37
+ * @throws Exception
38
+ *
39
+ * @return int
40
+ */
41
+ function random_int($min, $max)
42
+ {
43
+ /**
44
+ * Type and input logic checks
45
+ *
46
+ * If you pass it a float in the range (~PHP_INT_MAX, PHP_INT_MAX)
47
+ * (non-inclusive), it will sanely cast it to an int. If you it's equal to
48
+ * ~PHP_INT_MAX or PHP_INT_MAX, we let it fail as not an integer. Floats
49
+ * lose precision, so the <= and => operators might accidentally let a float
50
+ * through.
51
+ */
52
+
53
+ try {
54
+ $min = RandomCompat_intval($min);
55
+ } catch (TypeError $ex) {
56
+ throw new TypeError(
57
+ 'random_int(): $min must be an integer'
58
+ );
59
+ }
60
+
61
+ try {
62
+ $max = RandomCompat_intval($max);
63
+ } catch (TypeError $ex) {
64
+ throw new TypeError(
65
+ 'random_int(): $max must be an integer'
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Now that we've verified our weak typing system has given us an integer,
71
+ * let's validate the logic then we can move forward with generating random
72
+ * integers along a given range.
73
+ */
74
+ if ($min > $max) {
75
+ throw new Error(
76
+ 'Minimum value must be less than or equal to the maximum value'
77
+ );
78
+ }
79
+
80
+ if ($max === $min) {
81
+ return (int) $min;
82
+ }
83
+
84
+ /**
85
+ * Initialize variables to 0
86
+ *
87
+ * We want to store:
88
+ * $bytes => the number of random bytes we need
89
+ * $mask => an integer bitmask (for use with the &) operator
90
+ * so we can minimize the number of discards
91
+ */
92
+ $attempts = $bits = $bytes = $mask = $valueShift = 0;
93
+
94
+ /**
95
+ * At this point, $range is a positive number greater than 0. It might
96
+ * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to
97
+ * a float and we will lose some precision.
98
+ */
99
+ $range = $max - $min;
100
+
101
+ /**
102
+ * Test for integer overflow:
103
+ */
104
+ if (!is_int($range)) {
105
+
106
+ /**
107
+ * Still safely calculate wider ranges.
108
+ * Provided by @CodesInChaos, @oittaa
109
+ *
110
+ * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435
111
+ *
112
+ * We use ~0 as a mask in this case because it generates all 1s
113
+ *
114
+ * @ref https://eval.in/400356 (32-bit)
115
+ * @ref http://3v4l.org/XX9r5 (64-bit)
116
+ */
117
+ $bytes = PHP_INT_SIZE;
118
+ $mask = ~0;
119
+
120
+ } else {
121
+
122
+ /**
123
+ * $bits is effectively ceil(log($range, 2)) without dealing with
124
+ * type juggling
125
+ */
126
+ while ($range > 0) {
127
+ if ($bits % 8 === 0) {
128
+ ++$bytes;
129
+ }
130
+ ++$bits;
131
+ $range >>= 1;
132
+ $mask = $mask << 1 | 1;
133
+ }
134
+ $valueShift = $min;
135
+ }
136
+
137
+ $val = 0;
138
+ /**
139
+ * Now that we have our parameters set up, let's begin generating
140
+ * random integers until one falls between $min and $max
141
+ */
142
+ do {
143
+ /**
144
+ * The rejection probability is at most 0.5, so this corresponds
145
+ * to a failure probability of 2^-128 for a working RNG
146
+ */
147
+ if ($attempts > 128) {
148
+ throw new Exception(
149
+ 'random_int: RNG is broken - too many rejections'
150
+ );
151
+ }
152
+
153
+ /**
154
+ * Let's grab the necessary number of random bytes
155
+ */
156
+ $randomByteString = random_bytes($bytes);
157
+
158
+ /**
159
+ * Let's turn $randomByteString into an integer
160
+ *
161
+ * This uses bitwise operators (<< and |) to build an integer
162
+ * out of the values extracted from ord()
163
+ *
164
+ * Example: [9F] | [6D] | [32] | [0C] =>
165
+ * 159 + 27904 + 3276800 + 201326592 =>
166
+ * 204631455
167
+ */
168
+ $val &= 0;
169
+ for ($i = 0; $i < $bytes; ++$i) {
170
+ $val |= ord($randomByteString[$i]) << ($i * 8);
171
+ }
172
+
173
+ /**
174
+ * Apply mask
175
+ */
176
+ $val &= $mask;
177
+ $val += $valueShift;
178
+
179
+ ++$attempts;
180
+ /**
181
+ * If $val overflows to a floating point number,
182
+ * ... or is larger than $max,
183
+ * ... or smaller than $min,
184
+ * then try again.
185
+ */
186
+ } while (!is_int($val) || $val > $max || $val < $min);
187
+
188
+ return (int) $val;
189
+ }
190
+ }
vendor/paragonie/random_compat/other/build_phar.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $dist = dirname(__DIR__).'/dist';
3
+ if (!is_dir($dist)) {
4
+ mkdir($dist, 0755);
5
+ }
6
+ if (file_exists($dist.'/random_compat.phar')) {
7
+ unlink($dist.'/random_compat.phar');
8
+ }
9
+ $phar = new Phar(
10
+ $dist.'/random_compat.phar',
11
+ FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::KEY_AS_FILENAME,
12
+ 'random_compat.phar'
13
+ );
14
+ rename(
15
+ dirname(__DIR__).'/lib/random.php',
16
+ dirname(__DIR__).'/lib/index.php'
17
+ );
18
+ $phar->buildFromDirectory(dirname(__DIR__).'/lib');
19
+ rename(
20
+ dirname(__DIR__).'/lib/index.php',
21
+ dirname(__DIR__).'/lib/random.php'
22
+ );
23
+
24
+ /**
25
+ * If we pass an (optional) path to a private key as a second argument, we will
26
+ * sign the Phar with OpenSSL.
27
+ *
28
+ * If you leave this out, it will produce an unsigned .phar!
29
+ */
30
+ if ($argc > 1) {
31
+ if (!@is_readable($argv[1])) {
32
+ echo 'Could not read the private key file:', $argv[1], "\n";
33
+ exit(255);
34
+ }
35
+ $pkeyFile = file_get_contents($argv[1]);
36
+
37
+ $private = openssl_get_privatekey($pkeyFile);
38
+ if ($private !== false) {
39
+ $pkey = '';
40
+ openssl_pkey_export($private, $pkey);
41
+ $phar->setSignatureAlgorithm(Phar::OPENSSL, $pkey);
42
+
43
+ /**
44
+ * Save the corresponding public key to the file
45
+ */
46
+ if (!@is_readable($dist.'/random_compat.phar.pubkey')) {
47
+ $details = openssl_pkey_get_details($private);
48
+ file_put_contents(
49
+ $dist.'/random_compat.phar.pubkey',
50
+ $details['key']
51
+ );
52
+ }
53
+ } else {
54
+ echo 'An error occurred reading the private key from OpenSSL.', "\n";
55
+ exit(255);
56
+ }
57
+ }
vendor/paragonie/random_compat/psalm-autoload.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once 'lib/byte_safe_strings.php';
4
+ require_once 'lib/cast_to_int.php';
5
+ require_once 'lib/error_polyfill.php';
6
+ require_once 'other/ide_stubs/libsodium.php';
7
+ require_once 'lib/random.php';
8
+
9
+ $int = random_int(0, 65536);
vendor/paragonie/random_compat/psalm.xml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <psalm
3
+ autoloader="psalm-autoload.php"
4
+ stopOnFirstError="false"
5
+ useDocblockTypes="true"
6
+ >
7
+ <projectFiles>
8
+ <directory name="lib" />
9
+ </projectFiles>
10
+ <issueHandlers>
11
+ <DuplicateClass errorLevel="info" />
12
+ <InvalidOperand errorLevel="info" />
13
+ <UndefinedConstant errorLevel="info" />
14
+ <MissingReturnType errorLevel="info" />
15
+ </issueHandlers>
16
+ </psalm>
vendor/symfony/finder/.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ vendor/
2
+ composer.lock
3
+ phpunit.xml
vendor/symfony/finder/CHANGELOG.md ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ CHANGELOG
2
+ =========
3
+
4
+ 3.4.0
5
+ -----
6
+
7
+ * deprecated `Symfony\Component\Finder\Iterator\FilterIterator`
8
+ * added Finder::hasResults() method to check if any results were found
9
+
10
+ 3.3.0
11
+ -----
12
+
13
+ * added double-star matching to Glob::toRegex()
14
+
15
+ 3.0.0
16
+ -----
17
+
18
+ * removed deprecated classes
19
+
20
+ 2.8.0
21
+ -----
22
+
23
+ * deprecated adapters and related classes
24
+
25
+ 2.5.0
26
+ -----
27
+ * added support for GLOB_BRACE in the paths passed to Finder::in()
28
+
29
+ 2.3.0
30
+ -----
31
+
32
+ * added a way to ignore unreadable directories (via Finder::ignoreUnreadableDirs())
33
+ * unified the way subfolders that are not executable are handled by always throwing an AccessDeniedException exception
34
+
35
+ 2.2.0
36
+ -----
37
+
38
+ * added Finder::path() and Finder::notPath() methods
39
+ * added finder adapters to improve performance on specific platforms
40
+ * added support for wildcard characters (glob patterns) in the paths passed
41
+ to Finder::in()
42
+
43
+ 2.1.0
44
+ -----
45
+
46
+ * added Finder::sortByAccessedTime(), Finder::sortByChangedTime(), and
47
+ Finder::sortByModifiedTime()
48
+ * added Countable to Finder
49
+ * added support for an array of directories as an argument to
50
+ Finder::exclude()
51
+ * added searching based on the file content via Finder::contains() and
52
+ Finder::notContains()
53
+ * added support for the != operator in the Comparator
54
+ * [BC BREAK] filter expressions (used for file name and content) are no more
55
+ considered as regexps but glob patterns when they are enclosed in '*' or '?'
vendor/symfony/finder/Comparator/Comparator.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Comparator;
13
+
14
+ /**
15
+ * Comparator.
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ */
19
+ class Comparator
20
+ {
21
+ private $target;
22
+ private $operator = '==';
23
+
24
+ /**
25
+ * Gets the target value.
26
+ *
27
+ * @return string The target value
28
+ */
29
+ public function getTarget()
30
+ {
31
+ return $this->target;
32
+ }
33
+
34
+ /**
35
+ * Sets the target value.
36
+ *
37
+ * @param string $target The target value
38
+ */
39
+ public function setTarget($target)
40
+ {
41
+ $this->target = $target;
42
+ }
43
+
44
+ /**
45
+ * Gets the comparison operator.
46
+ *
47
+ * @return string The operator
48
+ */
49
+ public function getOperator()
50
+ {
51
+ return $this->operator;
52
+ }
53
+
54
+ /**
55
+ * Sets the comparison operator.
56
+ *
57
+ * @param string $operator A valid operator
58
+ *
59
+ * @throws \InvalidArgumentException
60
+ */
61
+ public function setOperator($operator)
62
+ {
63
+ if (!$operator) {
64
+ $operator = '==';
65
+ }
66
+
67
+ if (!in_array($operator, array('>', '<', '>=', '<=', '==', '!='))) {
68
+ throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator));
69
+ }
70
+
71
+ $this->operator = $operator;
72
+ }
73
+
74
+ /**
75
+ * Tests against the target.
76
+ *
77
+ * @param mixed $test A test value
78
+ *
79
+ * @return bool
80
+ */
81
+ public function test($test)
82
+ {
83
+ switch ($this->operator) {
84
+ case '>':
85
+ return $test > $this->target;
86
+ case '>=':
87
+ return $test >= $this->target;
88
+ case '<':
89
+ return $test < $this->target;
90
+ case '<=':
91
+ return $test <= $this->target;
92
+ case '!=':
93
+ return $test != $this->target;
94
+ }
95
+
96
+ return $test == $this->target;
97
+ }
98
+ }
vendor/symfony/finder/Comparator/DateComparator.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Comparator;
13
+
14
+ /**
15
+ * DateCompare compiles date comparisons.
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ */
19
+ class DateComparator extends Comparator
20
+ {
21
+ /**
22
+ * @param string $test A comparison string
23
+ *
24
+ * @throws \InvalidArgumentException If the test is not understood
25
+ */
26
+ public function __construct($test)
27
+ {
28
+ if (!preg_match('#^\s*(==|!=|[<>]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) {
29
+ throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test));
30
+ }
31
+
32
+ try {
33
+ $date = new \DateTime($matches[2]);
34
+ $target = $date->format('U');
35
+ } catch (\Exception $e) {
36
+ throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2]));
37
+ }
38
+
39
+ $operator = isset($matches[1]) ? $matches[1] : '==';
40
+ if ('since' === $operator || 'after' === $operator) {
41
+ $operator = '>';
42
+ }
43
+
44
+ if ('until' === $operator || 'before' === $operator) {
45
+ $operator = '<';
46
+ }
47
+
48
+ $this->setOperator($operator);
49
+ $this->setTarget($target);
50
+ }
51
+ }
vendor/symfony/finder/Comparator/NumberComparator.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Comparator;
13
+
14
+ /**
15
+ * NumberComparator compiles a simple comparison to an anonymous
16
+ * subroutine, which you can call with a value to be tested again.
17
+ *
18
+ * Now this would be very pointless, if NumberCompare didn't understand
19
+ * magnitudes.
20
+ *
21
+ * The target value may use magnitudes of kilobytes (k, ki),
22
+ * megabytes (m, mi), or gigabytes (g, gi). Those suffixed
23
+ * with an i use the appropriate 2**n version in accordance with the
24
+ * IEC standard: http://physics.nist.gov/cuu/Units/binary.html
25
+ *
26
+ * Based on the Perl Number::Compare module.
27
+ *
28
+ * @author Fabien Potencier <fabien@symfony.com> PHP port
29
+ * @author Richard Clamp <richardc@unixbeard.net> Perl version
30
+ * @copyright 2004-2005 Fabien Potencier <fabien@symfony.com>
31
+ * @copyright 2002 Richard Clamp <richardc@unixbeard.net>
32
+ *
33
+ * @see http://physics.nist.gov/cuu/Units/binary.html
34
+ */
35
+ class NumberComparator extends Comparator
36
+ {
37
+ /**
38
+ * @param string|int $test A comparison string or an integer
39
+ *
40
+ * @throws \InvalidArgumentException If the test is not understood
41
+ */
42
+ public function __construct($test)
43
+ {
44
+ if (!preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) {
45
+ throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test));
46
+ }
47
+
48
+ $target = $matches[2];
49
+ if (!is_numeric($target)) {
50
+ throw new \InvalidArgumentException(sprintf('Invalid number "%s".', $target));
51
+ }
52
+ if (isset($matches[3])) {
53
+ // magnitude
54
+ switch (strtolower($matches[3])) {
55
+ case 'k':
56
+ $target *= 1000;
57
+ break;
58
+ case 'ki':
59
+ $target *= 1024;
60
+ break;
61
+ case 'm':
62
+ $target *= 1000000;
63
+ break;
64
+ case 'mi':
65
+ $target *= 1024 * 1024;
66
+ break;
67
+ case 'g':
68
+ $target *= 1000000000;
69
+ break;
70
+ case 'gi':
71
+ $target *= 1024 * 1024 * 1024;
72
+ break;
73
+ }
74
+ }
75
+
76
+ $this->setTarget($target);
77
+ $this->setOperator(isset($matches[1]) ? $matches[1] : '==');
78
+ }
79
+ }
vendor/symfony/finder/Exception/AccessDeniedException.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Exception;
13
+
14
+ /**
15
+ * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
16
+ */
17
+ class AccessDeniedException extends \UnexpectedValueException
18
+ {
19
+ }
vendor/symfony/finder/Exception/ExceptionInterface.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Exception;
13
+
14
+ /**
15
+ * @author Jean-François Simon <contact@jfsimon.fr>
16
+ *
17
+ * @deprecated since 3.3, to be removed in 4.0.
18
+ */
19
+ interface ExceptionInterface
20
+ {
21
+ /**
22
+ * @return \Symfony\Component\Finder\Adapter\AdapterInterface
23
+ */
24
+ public function getAdapter();
25
+ }
vendor/symfony/finder/Finder.php ADDED
@@ -0,0 +1,731 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder;
13
+
14
+ use Symfony\Component\Finder\Comparator\DateComparator;
15
+ use Symfony\Component\Finder\Comparator\NumberComparator;
16
+ use Symfony\Component\Finder\Iterator\CustomFilterIterator;
17
+ use Symfony\Component\Finder\Iterator\DateRangeFilterIterator;
18
+ use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator;
19
+ use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator;
20
+ use Symfony\Component\Finder\Iterator\FilecontentFilterIterator;
21
+ use Symfony\Component\Finder\Iterator\FilenameFilterIterator;
22
+ use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator;
23
+ use Symfony\Component\Finder\Iterator\SortableIterator;
24
+
25
+ /**
26
+ * Finder allows to build rules to find files and directories.
27
+ *
28
+ * It is a thin wrapper around several specialized iterator classes.
29
+ *
30
+ * All rules may be invoked several times.
31
+ *
32
+ * All methods return the current Finder object to allow easy chaining:
33
+ *
34
+ * $finder = Finder::create()->files()->name('*.php')->in(__DIR__);
35
+ *
36
+ * @author Fabien Potencier <fabien@symfony.com>
37
+ */
38
+ class Finder implements \IteratorAggregate, \Countable
39
+ {
40
+ const IGNORE_VCS_FILES = 1;
41
+ const IGNORE_DOT_FILES = 2;
42
+
43
+ private $mode = 0;
44
+ private $names = array();
45
+ private $notNames = array();
46
+ private $exclude = array();
47
+ private $filters = array();
48
+ private $depths = array();
49
+ private $sizes = array();
50
+ private $followLinks = false;
51
+ private $sort = false;
52
+ private $ignore = 0;
53
+ private $dirs = array();
54
+ private $dates = array();
55
+ private $iterators = array();
56
+ private $contains = array();
57
+ private $notContains = array();
58
+ private $paths = array();
59
+ private $notPaths = array();
60
+ private $ignoreUnreadableDirs = false;
61
+
62
+ private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg');
63
+
64
+ public function __construct()
65
+ {
66
+ $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES;
67
+ }
68
+
69
+ /**
70
+ * Creates a new Finder.
71
+ *
72
+ * @return static
73
+ */
74
+ public static function create()
75
+ {
76
+ return new static();
77
+ }
78
+
79
+ /**
80
+ * Restricts the matching to directories only.
81
+ *
82
+ * @return $this
83
+ */
84
+ public function directories()
85
+ {
86
+ $this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES;
87
+
88
+ return $this;
89
+ }
90
+
91
+ /**
92
+ * Restricts the matching to files only.
93
+ *
94
+ * @return $this
95
+ */
96
+ public function files()
97
+ {
98
+ $this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES;
99
+
100
+ return $this;
101
+ }
102
+
103
+ /**
104
+ * Adds tests for the directory depth.
105
+ *
106
+ * Usage:
107
+ *
108
+ * $finder->depth('> 1') // the Finder will start matching at level 1.
109
+ * $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point.
110
+ *
111
+ * @param string|int $level The depth level expression
112
+ *
113
+ * @return $this
114
+ *
115
+ * @see DepthRangeFilterIterator
116
+ * @see NumberComparator
117
+ */
118
+ public function depth($level)
119
+ {
120
+ $this->depths[] = new Comparator\NumberComparator($level);
121
+
122
+ return $this;
123
+ }
124
+
125
+ /**
126
+ * Adds tests for file dates (last modified).
127
+ *
128
+ * The date must be something that strtotime() is able to parse:
129
+ *
130
+ * $finder->date('since yesterday');
131
+ * $finder->date('until 2 days ago');
132
+ * $finder->date('> now - 2 hours');
133
+ * $finder->date('>= 2005-10-15');
134
+ *
135
+ * @param string $date A date range string
136
+ *
137
+ * @return $this
138
+ *
139
+ * @see strtotime
140
+ * @see DateRangeFilterIterator
141
+ * @see DateComparator
142
+ */
143
+ public function date($date)
144
+ {
145
+ $this->dates[] = new Comparator\DateComparator($date);
146
+
147
+ return $this;
148
+ }
149
+
150
+ /**
151
+ * Adds rules that files must match.
152
+ *
153
+ * You can use patterns (delimited with / sign), globs or simple strings.
154
+ *
155
+ * $finder->name('*.php')
156
+ * $finder->name('/\.php$/') // same as above
157
+ * $finder->name('test.php')
158
+ *
159
+ * @param string $pattern A pattern (a regexp, a glob, or a string)
160
+ *
161
+ * @return $this
162
+ *
163
+ * @see FilenameFilterIterator
164
+ */
165
+ public function name($pattern)
166
+ {
167
+ $this->names[] = $pattern;
168
+
169
+ return $this;
170
+ }
171
+
172
+ /**
173
+ * Adds rules that files must not match.
174
+ *
175
+ * @param string $pattern A pattern (a regexp, a glob, or a string)
176
+ *
177
+ * @return $this
178
+ *
179
+ * @see FilenameFilterIterator
180
+ */
181
+ public function notName($pattern)
182
+ {
183
+ $this->notNames[] = $pattern;
184
+
185
+ return $this;
186
+ }
187
+
188
+ /**
189
+ * Adds tests that file contents must match.
190
+ *
191
+ * Strings or PCRE patterns can be used:
192
+ *
193
+ * $finder->contains('Lorem ipsum')
194
+ * $finder->contains('/Lorem ipsum/i')
195
+ *
196
+ * @param string $pattern A pattern (string or regexp)
197
+ *
198
+ * @return $this
199
+ *
200
+ * @see FilecontentFilterIterator
201
+ */
202
+ public function contains($pattern)
203
+ {
204
+ $this->contains[] = $pattern;
205
+
206
+ return $this;
207
+ }
208
+
209
+ /**
210
+ * Adds tests that file contents must not match.
211
+ *
212
+ * Strings or PCRE patterns can be used:
213
+ *
214
+ * $finder->notContains('Lorem ipsum')
215
+ * $finder->notContains('/Lorem ipsum/i')
216
+ *
217
+ * @param string $pattern A pattern (string or regexp)
218
+ *
219
+ * @return $this
220
+ *
221
+ * @see FilecontentFilterIterator
222
+ */
223
+ public function notContains($pattern)
224
+ {
225
+ $this->notContains[] = $pattern;
226
+
227
+ return $this;
228
+ }
229
+
230
+ /**
231
+ * Adds rules that filenames must match.
232
+ *
233
+ * You can use patterns (delimited with / sign) or simple strings.
234
+ *
235
+ * $finder->path('some/special/dir')
236
+ * $finder->path('/some\/special\/dir/') // same as above
237
+ *
238
+ * Use only / as dirname separator.
239
+ *
240
+ * @param string $pattern A pattern (a regexp or a string)
241
+ *
242
+ * @return $this
243
+ *
244
+ * @see FilenameFilterIterator
245
+ */
246
+ public function path($pattern)
247
+ {
248
+ $this->paths[] = $pattern;
249
+
250
+ return $this;
251
+ }
252
+
253
+ /**
254
+ * Adds rules that filenames must not match.
255
+ *
256
+ * You can use patterns (delimited with / sign) or simple strings.
257
+ *
258
+ * $finder->notPath('some/special/dir')
259
+ * $finder->notPath('/some\/special\/dir/') // same as above
260
+ *
261
+ * Use only / as dirname separator.
262
+ *
263
+ * @param string $pattern A pattern (a regexp or a string)
264
+ *
265
+ * @return $this
266
+ *
267
+ * @see FilenameFilterIterator
268
+ */
269
+ public function notPath($pattern)
270
+ {
271
+ $this->notPaths[] = $pattern;
272
+
273
+ return $this;
274
+ }
275
+
276
+ /**
277
+ * Adds tests for file sizes.
278
+ *
279
+ * $finder->size('> 10K');
280
+ * $finder->size('<= 1Ki');
281
+ * $finder->size(4);
282
+ *
283
+ * @param string|int $size A size range string or an integer
284
+ *
285
+ * @return $this
286
+ *
287
+ * @see SizeRangeFilterIterator
288
+ * @see NumberComparator
289
+ */
290
+ public function size($size)
291
+ {
292
+ $this->sizes[] = new Comparator\NumberComparator($size);
293
+
294
+ return $this;
295
+ }
296
+
297
+ /**
298
+ * Excludes directories.
299
+ *
300
+ * Directories passed as argument must be relative to the ones defined with the `in()` method. For example:
301
+ *
302
+ * $finder->in(__DIR__)->exclude('ruby');
303
+ *
304
+ * @param string|array $dirs A directory path or an array of directories
305
+ *
306
+ * @return $this
307
+ *
308
+ * @see ExcludeDirectoryFilterIterator
309
+ */
310
+ public function exclude($dirs)
311
+ {
312
+ $this->exclude = array_merge($this->exclude, (array) $dirs);
313
+
314
+ return $this;
315
+ }
316
+
317
+ /**
318
+ * Excludes "hidden" directories and files (starting with a dot).
319
+ *
320
+ * This option is enabled by default.
321
+ *
322
+ * @param bool $ignoreDotFiles Whether to exclude "hidden" files or not
323
+ *
324
+ * @return $this
325
+ *
326
+ * @see ExcludeDirectoryFilterIterator
327
+ */
328
+ public function ignoreDotFiles($ignoreDotFiles)
329
+ {
330
+ if ($ignoreDotFiles) {
331
+ $this->ignore |= static::IGNORE_DOT_FILES;
332
+ } else {
333
+ $this->ignore &= ~static::IGNORE_DOT_FILES;
334
+ }
335
+
336
+ return $this;
337
+ }
338
+
339
+ /**
340
+ * Forces the finder to ignore version control directories.
341
+ *
342
+ * This option is enabled by default.
343
+ *
344
+ * @param bool $ignoreVCS Whether to exclude VCS files or not
345
+ *
346
+ * @return $this
347
+ *
348
+ * @see ExcludeDirectoryFilterIterator
349
+ */
350
+ public function ignoreVCS($ignoreVCS)
351
+ {
352
+ if ($ignoreVCS) {
353
+ $this->ignore |= static::IGNORE_VCS_FILES;
354
+ } else {
355
+ $this->ignore &= ~static::IGNORE_VCS_FILES;
356
+ }
357
+
358
+ return $this;
359
+ }
360
+
361
+ /**
362
+ * Adds VCS patterns.
363
+ *
364
+ * @see ignoreVCS()
365
+ *
366
+ * @param string|string[] $pattern VCS patterns to ignore
367
+ */
368
+ public static function addVCSPattern($pattern)
369
+ {
370
+ foreach ((array) $pattern as $p) {
371
+ self::$vcsPatterns[] = $p;
372
+ }
373
+
374
+ self::$vcsPatterns = array_unique(self::$vcsPatterns);
375
+ }
376
+
377
+ /**
378
+ * Sorts files and directories by an anonymous function.
379
+ *
380
+ * The anonymous function receives two \SplFileInfo instances to compare.
381
+ *
382
+ * This can be slow as all the matching files and directories must be retrieved for comparison.
383
+ *
384
+ * @return $this
385
+ *
386
+ * @see SortableIterator
387
+ */
388
+ public function sort(\Closure $closure)
389
+ {
390
+ $this->sort = $closure;
391
+
392
+ return $this;
393
+ }
394
+
395
+ /**
396
+ * Sorts files and directories by name.
397
+ *
398
+ * This can be slow as all the matching files and directories must be retrieved for comparison.
399
+ *
400
+ * @return $this
401
+ *
402
+ * @see SortableIterator
403
+ */
404
+ public function sortByName()
405
+ {
406
+ $this->sort = Iterator\SortableIterator::SORT_BY_NAME;
407
+
408
+ return $this;
409
+ }
410
+
411
+ /**
412
+ * Sorts files and directories by type (directories before files), then by name.
413
+ *
414
+ * This can be slow as all the matching files and directories must be retrieved for comparison.
415
+ *
416
+ * @return $this
417
+ *
418
+ * @see SortableIterator
419
+ */
420
+ public function sortByType()
421
+ {
422
+ $this->sort = Iterator\SortableIterator::SORT_BY_TYPE;
423
+
424
+ return $this;
425
+ }
426
+
427
+ /**
428
+ * Sorts files and directories by the last accessed time.
429
+ *
430
+ * This is the time that the file was last accessed, read or written to.
431
+ *
432
+ * This can be slow as all the matching files and directories must be retrieved for comparison.
433
+ *
434
+ * @return $this
435
+ *
436
+ * @see SortableIterator
437
+ */
438
+ public function sortByAccessedTime()
439
+ {
440
+ $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME;
441
+
442
+ return $this;
443
+ }
444
+
445
+ /**
446
+ * Sorts files and directories by the last inode changed time.
447
+ *
448
+ * This is the time that the inode information was last modified (permissions, owner, group or other metadata).
449
+ *
450
+ * On Windows, since inode is not available, changed time is actually the file creation time.
451
+ *
452
+ * This can be slow as all the matching files and directories must be retrieved for comparison.
453
+ *
454
+ * @return $this
455
+ *
456
+ * @see SortableIterator
457
+ */
458
+ public function sortByChangedTime()
459
+ {
460
+ $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME;
461
+
462
+ return $this;
463
+ }
464
+
465
+ /**
466
+ * Sorts files and directories by the last modified time.
467
+ *
468
+ * This is the last time the actual contents of the file were last modified.
469
+ *
470
+ * This can be slow as all the matching files and directories must be retrieved for comparison.
471
+ *
472
+ * @return $this
473
+ *
474
+ * @see SortableIterator
475
+ */
476
+ public function sortByModifiedTime()
477
+ {
478
+ $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME;
479
+
480
+ return $this;
481
+ }
482
+
483
+ /**
484
+ * Filters the iterator with an anonymous function.
485
+ *
486
+ * The anonymous function receives a \SplFileInfo and must return false
487
+ * to remove files.
488
+ *
489
+ * @return $this
490
+ *
491
+ * @see CustomFilterIterator
492
+ */
493
+ public function filter(\Closure $closure)
494
+ {
495
+ $this->filters[] = $closure;
496
+
497
+ return $this;
498
+ }
499
+
500
+ /**
501
+ * Forces the following of symlinks.
502
+ *
503
+ * @return $this
504
+ */
505
+ public function followLinks()
506
+ {
507
+ $this->followLinks = true;
508
+
509
+ return $this;
510
+ }
511
+
512
+ /**
513
+ * Tells finder to ignore unreadable directories.
514
+ *
515
+ * By default, scanning unreadable directories content throws an AccessDeniedException.
516
+ *
517
+ * @param bool $ignore
518
+ *
519
+ * @return $this
520
+ */
521
+ public function ignoreUnreadableDirs($ignore = true)
522
+ {
523
+ $this->ignoreUnreadableDirs = (bool) $ignore;
524
+
525
+ return $this;
526
+ }
527
+
528
+ /**
529
+ * Searches files and directories which match defined rules.
530
+ *
531
+ * @param string|array $dirs A directory path or an array of directories
532
+ *
533
+ * @return $this
534
+ *
535
+ * @throws \InvalidArgumentException if one of the directories does not exist
536
+ */
537
+ public function in($dirs)
538
+ {
539
+ $resolvedDirs = array();
540
+
541
+ foreach ((array) $dirs as $dir) {
542
+ if (is_dir($dir)) {
543
+ $resolvedDirs[] = $dir;
544
+ } elseif ($glob = glob($dir, (defined('GLOB_BRACE') ? GLOB_BRACE : 0) | GLOB_ONLYDIR)) {
545
+ $resolvedDirs = array_merge($resolvedDirs, $glob);
546
+ } else {
547
+ throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir));
548
+ }
549
+ }
550
+
551
+ $this->dirs = array_merge($this->dirs, $resolvedDirs);
552
+
553
+ return $this;
554
+ }
555
+
556
+ /**
557
+ * Returns an Iterator for the current Finder configuration.
558
+ *
559
+ * This method implements the IteratorAggregate interface.
560
+ *
561
+ * @return \Iterator|SplFileInfo[] An iterator
562
+ *
563
+ * @throws \LogicException if the in() method has not been called
564
+ */
565
+ public function getIterator()
566
+ {
567
+ if (0 === count($this->dirs) && 0 === count($this->iterators)) {
568
+ throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.');
569
+ }
570
+
571
+ if (1 === count($this->dirs) && 0 === count($this->iterators)) {
572
+ return $this->searchInDirectory($this->dirs[0]);
573
+ }
574
+
575
+ $iterator = new \AppendIterator();
576
+ foreach ($this->dirs as $dir) {
577
+ $iterator->append($this->searchInDirectory($dir));
578
+ }
579
+
580
+ foreach ($this->iterators as $it) {
581
+ $iterator->append($it);
582
+ }
583
+
584
+ return $iterator;
585
+ }
586
+
587
+ /**
588
+ * Appends an existing set of files/directories to the finder.
589
+ *
590
+ * The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array.
591
+ *
592
+ * @param mixed $iterator
593
+ *
594
+ * @return $this
595
+ *
596
+ * @throws \InvalidArgumentException when the given argument is not iterable
597
+ */
598
+ public function append($iterator)
599
+ {
600
+ if ($iterator instanceof \IteratorAggregate) {
601
+ $this->iterators[] = $iterator->getIterator();
602
+ } elseif ($iterator instanceof \Iterator) {
603
+ $this->iterators[] = $iterator;
604
+ } elseif ($iterator instanceof \Traversable || is_array($iterator)) {
605
+ $it = new \ArrayIterator();
606
+ foreach ($iterator as $file) {
607
+ $it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file));
608
+ }
609
+ $this->iterators[] = $it;
610
+ } else {
611
+ throw new \InvalidArgumentException('Finder::append() method wrong argument type.');
612
+ }
613
+
614
+ return $this;
615
+ }
616
+
617
+ /**
618
+ * Check if the any results were found.
619
+ *
620
+ * @return bool
621
+ */
622
+ public function hasResults()
623
+ {
624
+ foreach ($this->getIterator() as $_) {
625
+ return true;
626
+ }
627
+
628
+ return false;
629
+ }
630
+
631
+ /**
632
+ * Counts all the results collected by the iterators.
633
+ *
634
+ * @return int
635
+ */
636
+ public function count()
637
+ {
638
+ return iterator_count($this->getIterator());
639
+ }
640
+
641
+ /**
642
+ * @param $dir
643
+ *
644
+ * @return \Iterator
645
+ */
646
+ private function searchInDirectory($dir)
647
+ {
648
+ if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) {
649
+ $this->exclude = array_merge($this->exclude, self::$vcsPatterns);
650
+ }
651
+
652
+ if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) {
653
+ $this->notPaths[] = '#(^|/)\..+(/|$)#';
654
+ }
655
+
656
+ $minDepth = 0;
657
+ $maxDepth = PHP_INT_MAX;
658
+
659
+ foreach ($this->depths as $comparator) {
660
+ switch ($comparator->getOperator()) {
661
+ case '>':
662
+ $minDepth = $comparator->getTarget() + 1;
663
+ break;
664
+ case '>=':
665
+ $minDepth = $comparator->getTarget();
666
+ break;
667
+ case '<':
668
+ $maxDepth = $comparator->getTarget() - 1;
669
+ break;
670
+ case '<=':
671
+ $maxDepth = $comparator->getTarget();
672
+ break;
673
+ default:
674
+ $minDepth = $maxDepth = $comparator->getTarget();
675
+ }
676
+ }
677
+
678
+ $flags = \RecursiveDirectoryIterator::SKIP_DOTS;
679
+
680
+ if ($this->followLinks) {
681
+ $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS;
682
+ }
683
+
684
+ $iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs);
685
+
686
+ if ($this->exclude) {
687
+ $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude);
688
+ }
689
+
690
+ $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);
691
+
692
+ if ($minDepth > 0 || $maxDepth < PHP_INT_MAX) {
693
+ $iterator = new Iterator\DepthRangeFilterIterator($iterator, $minDepth, $maxDepth);
694
+ }
695
+
696
+ if ($this->mode) {
697
+ $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode);
698
+ }
699
+
700
+ if ($this->names || $this->notNames) {
701
+ $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames);
702
+ }
703
+
704
+ if ($this->contains || $this->notContains) {
705
+ $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains);
706
+ }
707
+
708
+ if ($this->sizes) {
709
+ $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes);
710
+ }
711
+
712
+ if ($this->dates) {
713
+ $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates);
714
+ }
715
+
716
+ if ($this->filters) {
717
+ $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters);
718
+ }
719
+
720
+ if ($this->paths || $this->notPaths) {
721
+ $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths);
722
+ }
723
+
724
+ if ($this->sort) {
725
+ $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort);
726
+ $iterator = $iteratorAggregate->getIterator();
727
+ }
728
+
729
+ return $iterator;
730
+ }
731
+ }
vendor/symfony/finder/Glob.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder;
13
+
14
+ /**
15
+ * Glob matches globbing patterns against text.
16
+ *
17
+ * if match_glob("foo.*", "foo.bar") echo "matched\n";
18
+ *
19
+ * // prints foo.bar and foo.baz
20
+ * $regex = glob_to_regex("foo.*");
21
+ * for (array('foo.bar', 'foo.baz', 'foo', 'bar') as $t)
22
+ * {
23
+ * if (/$regex/) echo "matched: $car\n";
24
+ * }
25
+ *
26
+ * Glob implements glob(3) style matching that can be used to match
27
+ * against text, rather than fetching names from a filesystem.
28
+ *
29
+ * Based on the Perl Text::Glob module.
30
+ *
31
+ * @author Fabien Potencier <fabien@symfony.com> PHP port
32
+ * @author Richard Clamp <richardc@unixbeard.net> Perl version
33
+ * @copyright 2004-2005 Fabien Potencier <fabien@symfony.com>
34
+ * @copyright 2002 Richard Clamp <richardc@unixbeard.net>
35
+ */
36
+ class Glob
37
+ {
38
+ /**
39
+ * Returns a regexp which is the equivalent of the glob pattern.
40
+ *
41
+ * @param string $glob The glob pattern
42
+ * @param bool $strictLeadingDot
43
+ * @param bool $strictWildcardSlash
44
+ * @param string $delimiter Optional delimiter
45
+ *
46
+ * @return string regex The regexp
47
+ */
48
+ public static function toRegex($glob, $strictLeadingDot = true, $strictWildcardSlash = true, $delimiter = '#')
49
+ {
50
+ $firstByte = true;
51
+ $escaping = false;
52
+ $inCurlies = 0;
53
+ $regex = '';
54
+ $sizeGlob = strlen($glob);
55
+ for ($i = 0; $i < $sizeGlob; ++$i) {
56
+ $car = $glob[$i];
57
+ if ($firstByte && $strictLeadingDot && '.' !== $car) {
58
+ $regex .= '(?=[^\.])';
59
+ }
60
+
61
+ $firstByte = '/' === $car;
62
+
63
+ if ($firstByte && $strictWildcardSlash && isset($glob[$i + 2]) && '**' === $glob[$i + 1].$glob[$i + 2] && (!isset($glob[$i + 3]) || '/' === $glob[$i + 3])) {
64
+ $car = '[^/]++/';
65
+ if (!isset($glob[$i + 3])) {
66
+ $car .= '?';
67
+ }
68
+
69
+ if ($strictLeadingDot) {
70
+ $car = '(?=[^\.])'.$car;
71
+ }
72
+
73
+ $car = '/(?:'.$car.')*';
74
+ $i += 2 + isset($glob[$i + 3]);
75
+
76
+ if ('/' === $delimiter) {
77
+ $car = str_replace('/', '\\/', $car);
78
+ }
79
+ }
80
+
81
+ if ($delimiter === $car || '.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) {
82
+ $regex .= "\\$car";
83
+ } elseif ('*' === $car) {
84
+ $regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*');
85
+ } elseif ('?' === $car) {
86
+ $regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.');
87
+ } elseif ('{' === $car) {
88
+ $regex .= $escaping ? '\\{' : '(';
89
+ if (!$escaping) {
90
+ ++$inCurlies;
91
+ }
92
+ } elseif ('}' === $car && $inCurlies) {
93
+ $regex .= $escaping ? '}' : ')';
94
+ if (!$escaping) {
95
+ --$inCurlies;
96
+ }
97
+ } elseif (',' === $car && $inCurlies) {
98
+ $regex .= $escaping ? ',' : '|';
99
+ } elseif ('\\' === $car) {
100
+ if ($escaping) {
101
+ $regex .= '\\\\';
102
+ $escaping = false;
103
+ } else {
104
+ $escaping = true;
105
+ }
106
+
107
+ continue;
108
+ } else {
109
+ $regex .= $car;
110
+ }
111
+ $escaping = false;
112
+ }
113
+
114
+ return $delimiter.'^'.$regex.'$'.$delimiter;
115
+ }
116
+ }
vendor/symfony/finder/Iterator/CustomFilterIterator.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Iterator;
13
+
14
+ /**
15
+ * CustomFilterIterator filters files by applying anonymous functions.
16
+ *
17
+ * The anonymous function receives a \SplFileInfo and must return false
18
+ * to remove files.
19
+ *
20
+ * @author Fabien Potencier <fabien@symfony.com>
21
+ */
22
+ class CustomFilterIterator extends FilterIterator
23
+ {
24
+ private $filters = array();
25
+
26
+ /**
27
+ * @param \Iterator $iterator The Iterator to filter
28
+ * @param callable[] $filters An array of PHP callbacks
29
+ *
30
+ * @throws \InvalidArgumentException
31
+ */
32
+ public function __construct(\Iterator $iterator, array $filters)
33
+ {
34
+ foreach ($filters as $filter) {
35
+ if (!is_callable($filter)) {
36
+ throw new \InvalidArgumentException('Invalid PHP callback.');
37
+ }
38
+ }
39
+ $this->filters = $filters;
40
+
41
+ parent::__construct($iterator);
42
+ }
43
+
44
+ /**
45
+ * Filters the iterator values.
46
+ *
47
+ * @return bool true if the value should be kept, false otherwise
48
+ */
49
+ public function accept()
50
+ {
51
+ $fileinfo = $this->current();
52
+
53
+ foreach ($this->filters as $filter) {
54
+ if (false === call_user_func($filter, $fileinfo)) {
55
+ return false;
56
+ }
57
+ }
58
+
59
+ return true;
60
+ }
61
+ }
vendor/symfony/finder/Iterator/DateRangeFilterIterator.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Iterator;
13
+
14
+ use Symfony\Component\Finder\Comparator\DateComparator;
15
+
16
+ /**
17
+ * DateRangeFilterIterator filters out files that are not in the given date range (last modified dates).
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ */
21
+ class DateRangeFilterIterator extends FilterIterator
22
+ {
23
+ private $comparators = array();
24
+
25
+ /**
26
+ * @param \Iterator $iterator The Iterator to filter
27
+ * @param DateComparator[] $comparators An array of DateComparator instances
28
+ */
29
+ public function __construct(\Iterator $iterator, array $comparators)
30
+ {
31
+ $this->comparators = $comparators;
32
+
33
+ parent::__construct($iterator);
34
+ }
35
+
36
+ /**
37
+ * Filters the iterator values.
38
+ *
39
+ * @return bool true if the value should be kept, false otherwise
40
+ */
41
+ public function accept()
42
+ {
43
+ $fileinfo = $this->current();
44
+
45
+ if (!file_exists($fileinfo->getPathname())) {
46
+ return false;
47
+ }
48
+
49
+ $filedate = $fileinfo->getMTime();
50
+ foreach ($this->comparators as $compare) {
51
+ if (!$compare->test($filedate)) {
52
+ return false;
53
+ }
54
+ }
55
+
56
+ return true;
57
+ }
58
+ }
vendor/symfony/finder/Iterator/DepthRangeFilterIterator.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Iterator;
13
+
14
+ /**
15
+ * DepthRangeFilterIterator limits the directory depth.
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ */
19
+ class DepthRangeFilterIterator extends FilterIterator
20
+ {
21
+ private $minDepth = 0;
22
+
23
+ /**
24
+ * @param \RecursiveIteratorIterator $iterator The Iterator to filter
25
+ * @param int $minDepth The min depth
26
+ * @param int $maxDepth The max depth
27
+ */
28
+ public function __construct(\RecursiveIteratorIterator $iterator, $minDepth = 0, $maxDepth = PHP_INT_MAX)
29
+ {
30
+ $this->minDepth = $minDepth;
31
+ $iterator->setMaxDepth(PHP_INT_MAX === $maxDepth ? -1 : $maxDepth);
32
+
33
+ parent::__construct($iterator);
34
+ }
35
+
36
+ /**
37
+ * Filters the iterator values.
38
+ *
39
+ * @return bool true if the value should be kept, false otherwise
40
+ */
41
+ public function accept()
42
+ {
43
+ return $this->getInnerIterator()->getDepth() >= $this->minDepth;
44
+ }
45
+ }
vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Iterator;
13
+
14
+ /**
15
+ * ExcludeDirectoryFilterIterator filters out directories.
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ */
19
+ class ExcludeDirectoryFilterIterator extends FilterIterator implements \RecursiveIterator
20
+ {
21
+ private $iterator;
22
+ private $isRecursive;
23
+ private $excludedDirs = array();
24
+ private $excludedPattern;
25
+
26
+ /**
27
+ * @param \Iterator $iterator The Iterator to filter
28
+ * @param array $directories An array of directories to exclude
29
+ */
30
+ public function __construct(\Iterator $iterator, array $directories)
31
+ {
32
+ $this->iterator = $iterator;
33
+ $this->isRecursive = $iterator instanceof \RecursiveIterator;
34
+ $patterns = array();
35
+ foreach ($directories as $directory) {
36
+ $directory = rtrim($directory, '/');
37
+ if (!$this->isRecursive || false !== strpos($directory, '/')) {
38
+ $patterns[] = preg_quote($directory, '#');
39
+ } else {
40
+ $this->excludedDirs[$directory] = true;
41
+ }
42
+ }
43
+ if ($patterns) {
44
+ $this->excludedPattern = '#(?:^|/)(?:'.implode('|', $patterns).')(?:/|$)#';
45
+ }
46
+
47
+ parent::__construct($iterator);
48
+ }
49
+
50
+ /**
51
+ * Filters the iterator values.
52
+ *
53
+ * @return bool True if the value should be kept, false otherwise
54
+ */
55
+ public function accept()
56
+ {
57
+ if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) {
58
+ return false;
59
+ }
60
+
61
+ if ($this->excludedPattern) {
62
+ $path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath();
63
+ $path = str_replace('\\', '/', $path);
64
+
65
+ return !preg_match($this->excludedPattern, $path);
66
+ }
67
+
68
+ return true;
69
+ }
70
+
71
+ public function hasChildren()
72
+ {
73
+ return $this->isRecursive && $this->iterator->hasChildren();
74
+ }
75
+
76
+ public function getChildren()
77
+ {
78
+ $children = new self($this->iterator->getChildren(), array());
79
+ $children->excludedDirs = $this->excludedDirs;
80
+ $children->excludedPattern = $this->excludedPattern;
81
+
82
+ return $children;
83
+ }
84
+ }
vendor/symfony/finder/Iterator/FileTypeFilterIterator.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Iterator;
13
+
14
+ /**
15
+ * FileTypeFilterIterator only keeps files, directories, or both.
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ */
19
+ class FileTypeFilterIterator extends FilterIterator
20
+ {
21
+ const ONLY_FILES = 1;
22
+ const ONLY_DIRECTORIES = 2;
23
+
24
+ private $mode;
25
+
26
+ /**
27
+ * @param \Iterator $iterator The Iterator to filter
28
+ * @param int $mode The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES)
29
+ */
30
+ public function __construct(\Iterator $iterator, $mode)
31
+ {
32
+ $this->mode = $mode;
33
+
34
+ parent::__construct($iterator);
35
+ }
36
+
37
+ /**
38
+ * Filters the iterator values.
39
+ *
40
+ * @return bool true if the value should be kept, false otherwise
41
+ */
42
+ public function accept()
43
+ {
44
+ $fileinfo = $this->current();
45
+ if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) {
46
+ return false;
47
+ } elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir()) {
48
+ return false;
49
+ }
50
+
51
+ return true;
52
+ }
53
+ }
vendor/symfony/finder/Iterator/FilecontentFilterIterator.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Iterator;
13
+
14
+ /**
15
+ * FilecontentFilterIterator filters files by their contents using patterns (regexps or strings).
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ * @author Włodzimierz Gajda <gajdaw@gajdaw.pl>
19
+ */
20
+ class FilecontentFilterIterator extends MultiplePcreFilterIterator
21
+ {
22
+ /**
23
+ * Filters the iterator values.
24
+ *
25
+ * @return bool true if the value should be kept, false otherwise
26
+ */
27
+ public function accept()
28
+ {
29
+ if (!$this->matchRegexps && !$this->noMatchRegexps) {
30
+ return true;
31
+ }
32
+
33
+ $fileinfo = $this->current();
34
+
35
+ if ($fileinfo->isDir() || !$fileinfo->isReadable()) {
36
+ return false;
37
+ }
38
+
39
+ $content = $fileinfo->getContents();
40
+ if (!$content) {
41
+ return false;
42
+ }
43
+
44
+ return $this->isAccepted($content);
45
+ }
46
+
47
+ /**
48
+ * Converts string to regexp if necessary.
49
+ *
50
+ * @param string $str Pattern: string or regexp
51
+ *
52
+ * @return string regexp corresponding to a given string or regexp
53
+ */
54
+ protected function toRegex($str)
55
+ {
56
+ return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
57
+ }
58
+ }
vendor/symfony/finder/Iterator/FilenameFilterIterator.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Iterator;
13
+
14
+ use Symfony\Component\Finder\Glob;
15
+
16
+ /**
17
+ * FilenameFilterIterator filters files by patterns (a regexp, a glob, or a string).
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ */
21
+ class FilenameFilterIterator extends MultiplePcreFilterIterator
22
+ {
23
+ /**
24
+ * Filters the iterator values.
25
+ *
26
+ * @return bool true if the value should be kept, false otherwise
27
+ */
28
+ public function accept()
29
+ {
30
+ return $this->isAccepted($this->current()->getFilename());
31
+ }
32
+
33
+ /**
34
+ * Converts glob to regexp.
35
+ *
36
+ * PCRE patterns are left unchanged.
37
+ * Glob strings are transformed with Glob::toRegex().
38
+ *
39
+ * @param string $str Pattern: glob or regexp
40
+ *
41
+ * @return string regexp corresponding to a given glob or regexp
42
+ */
43
+ protected function toRegex($str)
44
+ {
45
+ return $this->isRegex($str) ? $str : Glob::toRegex($str);
46
+ }
47
+ }
vendor/symfony/finder/Iterator/FilterIterator.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Iterator;
13
+
14
+ /**
15
+ * This iterator just overrides the rewind method in order to correct a PHP bug,
16
+ * which existed before version 5.5.23/5.6.7.
17
+ *
18
+ * @see https://bugs.php.net/68557
19
+ *
20
+ * @author Alex Bogomazov
21
+ *
22
+ * @deprecated since 3.4, to be removed in 4.0.
23
+ */
24
+ abstract class FilterIterator extends \FilterIterator
25
+ {
26
+ /**
27
+ * This is a workaround for the problem with \FilterIterator leaving inner \FilesystemIterator in wrong state after
28
+ * rewind in some cases.
29
+ *
30
+ * @see FilterIterator::rewind()
31
+ */
32
+ public function rewind()
33
+ {
34
+ if (\PHP_VERSION_ID > 50607 || (\PHP_VERSION_ID > 50523 && \PHP_VERSION_ID < 50600)) {
35
+ parent::rewind();
36
+
37
+ return;
38
+ }
39
+
40
+ $iterator = $this;
41
+ while ($iterator instanceof \OuterIterator) {
42
+ $innerIterator = $iterator->getInnerIterator();
43
+
44
+ if ($innerIterator instanceof RecursiveDirectoryIterator) {
45
+ // this condition is necessary for iterators to work properly with non-local filesystems like ftp
46
+ if ($innerIterator->isRewindable()) {
47
+ $innerIterator->next();
48
+ $innerIterator->rewind();
49
+ }
50
+ } elseif ($innerIterator instanceof \FilesystemIterator) {
51
+ $innerIterator->next();
52
+ $innerIterator->rewind();
53
+ }
54
+
55
+ $iterator = $innerIterator;
56
+ }
57
+
58
+ parent::rewind();
59
+ }
60
+ }
vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Iterator;
13
+
14
+ /**
15
+ * MultiplePcreFilterIterator filters files using patterns (regexps, globs or strings).
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ */
19
+ abstract class MultiplePcreFilterIterator extends FilterIterator
20
+ {
21
+ protected $matchRegexps = array();
22
+ protected $noMatchRegexps = array();
23
+
24
+ /**
25
+ * @param \Iterator $iterator The Iterator to filter
26
+ * @param array $matchPatterns An array of patterns that need to match
27
+ * @param array $noMatchPatterns An array of patterns that need to not match
28
+ */
29
+ public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns)
30
+ {
31
+ foreach ($matchPatterns as $pattern) {
32
+ $this->matchRegexps[] = $this->toRegex($pattern);
33
+ }
34
+
35
+ foreach ($noMatchPatterns as $pattern) {
36
+ $this->noMatchRegexps[] = $this->toRegex($pattern);
37
+ }
38
+
39
+ parent::__construct($iterator);
40
+ }
41
+
42
+ /**
43
+ * Checks whether the string is accepted by the regex filters.
44
+ *
45
+ * If there is no regexps defined in the class, this method will accept the string.
46
+ * Such case can be handled by child classes before calling the method if they want to
47
+ * apply a different behavior.
48
+ *
49
+ * @param string $string The string to be matched against filters
50
+ *
51
+ * @return bool
52
+ */
53
+ protected function isAccepted($string)
54
+ {
55
+ // should at least not match one rule to exclude
56
+ foreach ($this->noMatchRegexps as $regex) {
57
+ if (preg_match($regex, $string)) {
58
+ return false;
59
+ }
60
+ }
61
+
62
+ // should at least match one rule
63
+ if ($this->matchRegexps) {
64
+ foreach ($this->matchRegexps as $regex) {
65
+ if (preg_match($regex, $string)) {
66
+ return true;
67
+ }
68
+ }
69
+
70
+ return false;
71
+ }
72
+
73
+ // If there is no match rules, the file is accepted
74
+ return true;
75
+ }
76
+
77
+ /**
78
+ * Checks whether the string is a regex.
79
+ *
80
+ * @param string $str
81
+ *
82
+ * @return bool Whether the given string is a regex
83
+ */
84
+ protected function isRegex($str)
85
+ {
86
+ if (preg_match('/^(.{3,}?)[imsxuADU]*$/', $str, $m)) {
87
+ $start = substr($m[1], 0, 1);
88
+ $end = substr($m[1], -1);
89
+
90
+ if ($start === $end) {
91
+ return !preg_match('/[*?[:alnum:] \\\\]/', $start);
92
+ }
93
+
94
+ foreach (array(array('{', '}'), array('(', ')'), array('[', ']'), array('<', '>')) as $delimiters) {
95
+ if ($start === $delimiters[0] && $end === $delimiters[1]) {
96
+ return true;
97
+ }
98
+ }
99
+ }
100
+
101
+ return false;
102
+ }
103
+
104
+ /**
105
+ * Converts string into regexp.
106
+ *
107
+ * @param string $str Pattern
108
+ *
109
+ * @return string regexp corresponding to a given string
110
+ */
111
+ abstract protected function toRegex($str);
112
+ }
vendor/symfony/finder/Iterator/PathFilterIterator.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Iterator;
13
+
14
+ /**
15
+ * PathFilterIterator filters files by path patterns (e.g. some/special/dir).
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ * @author Włodzimierz Gajda <gajdaw@gajdaw.pl>
19
+ */
20
+ class PathFilterIterator extends MultiplePcreFilterIterator
21
+ {
22
+ /**
23
+ * Filters the iterator values.
24
+ *
25
+ * @return bool true if the value should be kept, false otherwise
26
+ */
27
+ public function accept()
28
+ {
29
+ $filename = $this->current()->getRelativePathname();
30
+
31
+ if ('\\' === DIRECTORY_SEPARATOR) {
32
+ $filename = str_replace('\\', '/', $filename);
33
+ }
34
+
35
+ return $this->isAccepted($filename);
36
+ }
37
+
38
+ /**
39
+ * Converts strings to regexp.
40
+ *
41
+ * PCRE patterns are left unchanged.
42
+ *
43
+ * Default conversion:
44
+ * 'lorem/ipsum/dolor' ==> 'lorem\/ipsum\/dolor/'
45
+ *
46
+ * Use only / as directory separator (on Windows also).
47
+ *
48
+ * @param string $str Pattern: regexp or dirname
49
+ *
50
+ * @return string regexp corresponding to a given string or regexp
51
+ */
52
+ protected function toRegex($str)
53
+ {
54
+ return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/';
55
+ }
56
+ }
vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Iterator;
13
+
14
+ use Symfony\Component\Finder\Exception\AccessDeniedException;
15
+ use Symfony\Component\Finder\SplFileInfo;
16
+
17
+ /**
18
+ * Extends the \RecursiveDirectoryIterator to support relative paths.
19
+ *
20
+ * @author Victor Berchet <victor@suumit.com>
21
+ */
22
+ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
23
+ {
24
+ /**
25
+ * @var bool
26
+ */
27
+ private $ignoreUnreadableDirs;
28
+
29
+ /**
30
+ * @var bool
31
+ */
32
+ private $rewindable;
33
+
34
+ // these 3 properties take part of the performance optimization to avoid redoing the same work in all iterations
35
+ private $rootPath;
36
+ private $subPath;
37
+ private $directorySeparator = '/';
38
+
39
+ /**
40
+ * @param string $path
41
+ * @param int $flags
42
+ * @param bool $ignoreUnreadableDirs
43
+ *
44
+ * @throws \RuntimeException
45
+ */
46
+ public function __construct($path, $flags, $ignoreUnreadableDirs = false)
47
+ {
48
+ if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) {
49
+ throw new \RuntimeException('This iterator only support returning current as fileinfo.');
50
+ }
51
+
52
+ parent::__construct($path, $flags);
53
+ $this->ignoreUnreadableDirs = $ignoreUnreadableDirs;
54
+ $this->rootPath = $path;
55
+ if ('/' !== DIRECTORY_SEPARATOR && !($flags & self::UNIX_PATHS)) {
56
+ $this->directorySeparator = DIRECTORY_SEPARATOR;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Return an instance of SplFileInfo with support for relative paths.
62
+ *
63
+ * @return SplFileInfo File information
64
+ */
65
+ public function current()
66
+ {
67
+ // the logic here avoids redoing the same work in all iterations
68
+
69
+ if (null === $subPathname = $this->subPath) {
70
+ $subPathname = $this->subPath = (string) $this->getSubPath();
71
+ }
72
+ if ('' !== $subPathname) {
73
+ $subPathname .= $this->directorySeparator;
74
+ }
75
+ $subPathname .= $this->getFilename();
76
+
77
+ return new SplFileInfo($this->rootPath.$this->directorySeparator.$subPathname, $this->subPath, $subPathname);
78
+ }
79
+
80
+ /**
81
+ * @return \RecursiveIterator
82
+ *
83
+ * @throws AccessDeniedException
84
+ */
85
+ public function getChildren()
86
+ {
87
+ try {
88
+ $children = parent::getChildren();
89
+
90
+ if ($children instanceof self) {
91
+ // parent method will call the constructor with default arguments, so unreadable dirs won't be ignored anymore
92
+ $children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs;
93
+
94
+ // performance optimization to avoid redoing the same work in all children
95
+ $children->rewindable = &$this->rewindable;
96
+ $children->rootPath = $this->rootPath;
97
+ }
98
+
99
+ return $children;
100
+ } catch (\UnexpectedValueException $e) {
101
+ if ($this->ignoreUnreadableDirs) {
102
+ // If directory is unreadable and finder is set to ignore it, a fake empty content is returned.
103
+ return new \RecursiveArrayIterator(array());
104
+ } else {
105
+ throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);
106
+ }
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Do nothing for non rewindable stream.
112
+ */
113
+ public function rewind()
114
+ {
115
+ if (false === $this->isRewindable()) {
116
+ return;
117
+ }
118
+
119
+ // @see https://bugs.php.net/68557
120
+ if (\PHP_VERSION_ID < 50523 || \PHP_VERSION_ID >= 50600 && \PHP_VERSION_ID < 50607) {
121
+ parent::next();
122
+ }
123
+
124
+ parent::rewind();
125
+ }
126
+
127
+ /**
128
+ * Checks if the stream is rewindable.
129
+ *
130
+ * @return bool true when the stream is rewindable, false otherwise
131
+ */
132
+ public function isRewindable()
133
+ {
134
+ if (null !== $this->rewindable) {
135
+ return $this->rewindable;
136
+ }
137
+
138
+ // workaround for an HHVM bug, should be removed when https://github.com/facebook/hhvm/issues/7281 is fixed
139
+ if ('' === $this->getPath()) {
140
+ return $this->rewindable = false;
141
+ }
142
+
143
+ if (false !== $stream = @opendir($this->getPath())) {
144
+ $infos = stream_get_meta_data($stream);
145
+ closedir($stream);
146
+
147
+ if ($infos['seekable']) {
148
+ return $this->rewindable = true;
149
+ }
150
+ }
151
+
152
+ return $this->rewindable = false;
153
+ }
154
+ }
vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Iterator;
13
+
14
+ use Symfony\Component\Finder\Comparator\NumberComparator;
15
+
16
+ /**
17
+ * SizeRangeFilterIterator filters out files that are not in the given size range.
18
+ *
19
+ * @author Fabien Potencier <fabien@symfony.com>
20
+ */
21
+ class SizeRangeFilterIterator extends FilterIterator
22
+ {
23
+ private $comparators = array();
24
+
25
+ /**
26
+ * @param \Iterator $iterator The Iterator to filter
27
+ * @param NumberComparator[] $comparators An array of NumberComparator instances
28
+ */
29
+ public function __construct(\Iterator $iterator, array $comparators)
30
+ {
31
+ $this->comparators = $comparators;
32
+
33
+ parent::__construct($iterator);
34
+ }
35
+
36
+ /**
37
+ * Filters the iterator values.
38
+ *
39
+ * @return bool true if the value should be kept, false otherwise
40
+ */
41
+ public function accept()
42
+ {
43
+ $fileinfo = $this->current();
44
+ if (!$fileinfo->isFile()) {
45
+ return true;
46
+ }
47
+
48
+ $filesize = $fileinfo->getSize();
49
+ foreach ($this->comparators as $compare) {
50
+ if (!$compare->test($filesize)) {
51
+ return false;
52
+ }
53
+ }
54
+
55
+ return true;
56
+ }
57
+ }
vendor/symfony/finder/Iterator/SortableIterator.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Iterator;
13
+
14
+ /**
15
+ * SortableIterator applies a sort on a given Iterator.
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ */
19
+ class SortableIterator implements \IteratorAggregate
20
+ {
21
+ const SORT_BY_NAME = 1;
22
+ const SORT_BY_TYPE = 2;
23
+ const SORT_BY_ACCESSED_TIME = 3;
24
+ const SORT_BY_CHANGED_TIME = 4;
25
+ const SORT_BY_MODIFIED_TIME = 5;
26
+
27
+ private $iterator;
28
+ private $sort;
29
+
30
+ /**
31
+ * @param \Traversable $iterator The Iterator to filter
32
+ * @param int|callable $sort The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a PHP callback)
33
+ *
34
+ * @throws \InvalidArgumentException
35
+ */
36
+ public function __construct(\Traversable $iterator, $sort)
37
+ {
38
+ $this->iterator = $iterator;
39
+
40
+ if (self::SORT_BY_NAME === $sort) {
41
+ $this->sort = function ($a, $b) {
42
+ return strcmp($a->getRealpath() ?: $a->getPathname(), $b->getRealpath() ?: $b->getPathname());
43
+ };
44
+ } elseif (self::SORT_BY_TYPE === $sort) {
45
+ $this->sort = function ($a, $b) {
46
+ if ($a->isDir() && $b->isFile()) {
47
+ return -1;
48
+ } elseif ($a->isFile() && $b->isDir()) {
49
+ return 1;
50
+ }
51
+
52
+ return strcmp($a->getRealpath() ?: $a->getPathname(), $b->getRealpath() ?: $b->getPathname());
53
+ };
54
+ } elseif (self::SORT_BY_ACCESSED_TIME === $sort) {
55
+ $this->sort = function ($a, $b) {
56
+ return $a->getATime() - $b->getATime();
57
+ };
58
+ } elseif (self::SORT_BY_CHANGED_TIME === $sort) {
59
+ $this->sort = function ($a, $b) {
60
+ return $a->getCTime() - $b->getCTime();
61
+ };
62
+ } elseif (self::SORT_BY_MODIFIED_TIME === $sort) {
63
+ $this->sort = function ($a, $b) {
64
+ return $a->getMTime() - $b->getMTime();
65
+ };
66
+ } elseif (is_callable($sort)) {
67
+ $this->sort = $sort;
68
+ } else {
69
+ throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.');
70
+ }
71
+ }
72
+
73
+ public function getIterator()
74
+ {
75
+ $array = iterator_to_array($this->iterator, true);
76
+ uasort($array, $this->sort);
77
+
78
+ return new \ArrayIterator($array);
79
+ }
80
+ }
vendor/symfony/finder/LICENSE ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2004-2018 Fabien Potencier
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is furnished
8
+ to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
vendor/symfony/finder/README.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Finder Component
2
+ ================
3
+
4
+ The Finder component finds files and directories via an intuitive fluent
5
+ interface.
6
+
7
+ Resources
8
+ ---------
9
+
10
+ * [Documentation](https://symfony.com/doc/current/components/finder.html)
11
+ * [Contributing](https://symfony.com/doc/current/contributing/index.html)
12
+ * [Report issues](https://github.com/symfony/symfony/issues) and
13
+ [send Pull Requests](https://github.com/symfony/symfony/pulls)
14
+ in the [main Symfony repository](https://github.com/symfony/symfony)
vendor/symfony/finder/SplFileInfo.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder;
13
+
14
+ /**
15
+ * Extends \SplFileInfo to support relative paths.
16
+ *
17
+ * @author Fabien Potencier <fabien@symfony.com>
18
+ */
19
+ class SplFileInfo extends \SplFileInfo
20
+ {
21
+ private $relativePath;
22
+ private $relativePathname;
23
+
24
+ /**
25
+ * @param string $file The file name
26
+ * @param string $relativePath The relative path
27
+ * @param string $relativePathname The relative path name
28
+ */
29
+ public function __construct($file, $relativePath, $relativePathname)
30
+ {
31
+ parent::__construct($file);
32
+ $this->relativePath = $relativePath;
33
+ $this->relativePathname = $relativePathname;
34
+ }
35
+
36
+ /**
37
+ * Returns the relative path.
38
+ *
39
+ * This path does not contain the file name.
40
+ *
41
+ * @return string the relative path
42
+ */
43
+ public function getRelativePath()
44
+ {
45
+ return $this->relativePath;
46
+ }
47
+
48
+ /**
49
+ * Returns the relative path name.
50
+ *
51
+ * This path contains the file name.
52
+ *
53
+ * @return string the relative path name
54
+ */
55
+ public function getRelativePathname()
56
+ {
57
+ return $this->relativePathname;
58
+ }
59
+
60
+ /**
61
+ * Returns the contents of the file.
62
+ *
63
+ * @return string the contents of the file
64
+ *
65
+ * @throws \RuntimeException
66
+ */
67
+ public function getContents()
68
+ {
69
+ $level = error_reporting(0);
70
+ $content = file_get_contents($this->getPathname());
71
+ error_reporting($level);
72
+ if (false === $content) {
73
+ $error = error_get_last();
74
+ throw new \RuntimeException($error['message']);
75
+ }
76
+
77
+ return $content;
78
+ }
79
+ }
vendor/symfony/finder/Tests/Comparator/ComparatorTest.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Comparator;
13
+
14
+ use PHPUnit\Framework\TestCase;
15
+ use Symfony\Component\Finder\Comparator\Comparator;
16
+
17
+ class ComparatorTest extends TestCase
18
+ {
19
+ public function testGetSetOperator()
20
+ {
21
+ $comparator = new Comparator();
22
+ try {
23
+ $comparator->setOperator('foo');
24
+ $this->fail('->setOperator() throws an \InvalidArgumentException if the operator is not valid.');
25
+ } catch (\Exception $e) {
26
+ $this->assertInstanceOf('InvalidArgumentException', $e, '->setOperator() throws an \InvalidArgumentException if the operator is not valid.');
27
+ }
28
+
29
+ $comparator = new Comparator();
30
+ $comparator->setOperator('>');
31
+ $this->assertEquals('>', $comparator->getOperator(), '->getOperator() returns the current operator');
32
+ }
33
+
34
+ public function testGetSetTarget()
35
+ {
36
+ $comparator = new Comparator();
37
+ $comparator->setTarget(8);
38
+ $this->assertEquals(8, $comparator->getTarget(), '->getTarget() returns the target');
39
+ }
40
+
41
+ /**
42
+ * @dataProvider getTestData
43
+ */
44
+ public function testTest($operator, $target, $match, $noMatch)
45
+ {
46
+ $c = new Comparator();
47
+ $c->setOperator($operator);
48
+ $c->setTarget($target);
49
+
50
+ foreach ($match as $m) {
51
+ $this->assertTrue($c->test($m), '->test() tests a string against the expression');
52
+ }
53
+
54
+ foreach ($noMatch as $m) {
55
+ $this->assertFalse($c->test($m), '->test() tests a string against the expression');
56
+ }
57
+ }
58
+
59
+ public function getTestData()
60
+ {
61
+ return array(
62
+ array('<', '1000', array('500', '999'), array('1000', '1500')),
63
+ );
64
+ }
65
+ }
vendor/symfony/finder/Tests/Comparator/DateComparatorTest.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Comparator;
13
+
14
+ use PHPUnit\Framework\TestCase;
15
+ use Symfony\Component\Finder\Comparator\DateComparator;
16
+
17
+ class DateComparatorTest extends TestCase
18
+ {
19
+ public function testConstructor()
20
+ {
21
+ try {
22
+ new DateComparator('foobar');
23
+ $this->fail('__construct() throws an \InvalidArgumentException if the test expression is not valid.');
24
+ } catch (\Exception $e) {
25
+ $this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the test expression is not valid.');
26
+ }
27
+
28
+ try {
29
+ new DateComparator('');
30
+ $this->fail('__construct() throws an \InvalidArgumentException if the test expression is not valid.');
31
+ } catch (\Exception $e) {
32
+ $this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the test expression is not valid.');
33
+ }
34
+ }
35
+
36
+ /**
37
+ * @dataProvider getTestData
38
+ */
39
+ public function testTest($test, $match, $noMatch)
40
+ {
41
+ $c = new DateComparator($test);
42
+
43
+ foreach ($match as $m) {
44
+ $this->assertTrue($c->test($m), '->test() tests a string against the expression');
45
+ }
46
+
47
+ foreach ($noMatch as $m) {
48
+ $this->assertFalse($c->test($m), '->test() tests a string against the expression');
49
+ }
50
+ }
51
+
52
+ public function getTestData()
53
+ {
54
+ return array(
55
+ array('< 2005-10-10', array(strtotime('2005-10-09')), array(strtotime('2005-10-15'))),
56
+ array('until 2005-10-10', array(strtotime('2005-10-09')), array(strtotime('2005-10-15'))),
57
+ array('before 2005-10-10', array(strtotime('2005-10-09')), array(strtotime('2005-10-15'))),
58
+ array('> 2005-10-10', array(strtotime('2005-10-15')), array(strtotime('2005-10-09'))),
59
+ array('after 2005-10-10', array(strtotime('2005-10-15')), array(strtotime('2005-10-09'))),
60
+ array('since 2005-10-10', array(strtotime('2005-10-15')), array(strtotime('2005-10-09'))),
61
+ array('!= 2005-10-10', array(strtotime('2005-10-11')), array(strtotime('2005-10-10'))),
62
+ );
63
+ }
64
+ }
vendor/symfony/finder/Tests/Comparator/NumberComparatorTest.php ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Comparator;
13
+
14
+ use PHPUnit\Framework\TestCase;
15
+ use Symfony\Component\Finder\Comparator\NumberComparator;
16
+
17
+ class NumberComparatorTest extends TestCase
18
+ {
19
+ /**
20
+ * @dataProvider getConstructorTestData
21
+ */
22
+ public function testConstructor($successes, $failures)
23
+ {
24
+ foreach ($successes as $s) {
25
+ new NumberComparator($s);
26
+ }
27
+
28
+ foreach ($failures as $f) {
29
+ try {
30
+ new NumberComparator($f);
31
+ $this->fail('__construct() throws an \InvalidArgumentException if the test expression is not valid.');
32
+ } catch (\Exception $e) {
33
+ $this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the test expression is not valid.');
34
+ }
35
+ }
36
+ }
37
+
38
+ /**
39
+ * @dataProvider getTestData
40
+ */
41
+ public function testTest($test, $match, $noMatch)
42
+ {
43
+ $c = new NumberComparator($test);
44
+
45
+ foreach ($match as $m) {
46
+ $this->assertTrue($c->test($m), '->test() tests a string against the expression');
47
+ }
48
+
49
+ foreach ($noMatch as $m) {
50
+ $this->assertFalse($c->test($m), '->test() tests a string against the expression');
51
+ }
52
+ }
53
+
54
+ public function getTestData()
55
+ {
56
+ return array(
57
+ array('< 1000', array('500', '999'), array('1000', '1500')),
58
+
59
+ array('< 1K', array('500', '999'), array('1000', '1500')),
60
+ array('<1k', array('500', '999'), array('1000', '1500')),
61
+ array(' < 1 K ', array('500', '999'), array('1000', '1500')),
62
+ array('<= 1K', array('1000'), array('1001')),
63
+ array('> 1K', array('1001'), array('1000')),
64
+ array('>= 1K', array('1000'), array('999')),
65
+
66
+ array('< 1KI', array('500', '1023'), array('1024', '1500')),
67
+ array('<= 1KI', array('1024'), array('1025')),
68
+ array('> 1KI', array('1025'), array('1024')),
69
+ array('>= 1KI', array('1024'), array('1023')),
70
+
71
+ array('1KI', array('1024'), array('1023', '1025')),
72
+ array('==1KI', array('1024'), array('1023', '1025')),
73
+
74
+ array('==1m', array('1000000'), array('999999', '1000001')),
75
+ array('==1mi', array(1024 * 1024), array(1024 * 1024 - 1, 1024 * 1024 + 1)),
76
+
77
+ array('==1g', array('1000000000'), array('999999999', '1000000001')),
78
+ array('==1gi', array(1024 * 1024 * 1024), array(1024 * 1024 * 1024 - 1, 1024 * 1024 * 1024 + 1)),
79
+
80
+ array('!= 1000', array('500', '999'), array('1000')),
81
+ );
82
+ }
83
+
84
+ public function getConstructorTestData()
85
+ {
86
+ return array(
87
+ array(
88
+ array(
89
+ '1', '0',
90
+ '3.5', '33.55', '123.456', '123456.78',
91
+ '.1', '.123',
92
+ '.0', '0.0',
93
+ '1.', '0.', '123.',
94
+ '==1', '!=1', '<1', '>1', '<=1', '>=1',
95
+ '==1k', '==1ki', '==1m', '==1mi', '==1g', '==1gi',
96
+ '1k', '1ki', '1m', '1mi', '1g', '1gi',
97
+ ),
98
+ array(
99
+ false, null, '',
100
+ ' ', 'foobar',
101
+ '=1', '===1',
102
+ '0 . 1', '123 .45', '234. 567',
103
+ '..', '.0.', '0.1.2',
104
+ ),
105
+ ),
106
+ );
107
+ }
108
+ }
vendor/symfony/finder/Tests/FinderTest.php ADDED
@@ -0,0 +1,698 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests;
13
+
14
+ use Symfony\Component\Finder\Finder;
15
+
16
+ class FinderTest extends Iterator\RealIteratorTestCase
17
+ {
18
+ public function testCreate()
19
+ {
20
+ $this->assertInstanceOf('Symfony\Component\Finder\Finder', Finder::create());
21
+ }
22
+
23
+ public function testDirectories()
24
+ {
25
+ $finder = $this->buildFinder();
26
+ $this->assertSame($finder, $finder->directories());
27
+ $this->assertIterator($this->toAbsolute(array('foo', 'toto')), $finder->in(self::$tmpDir)->getIterator());
28
+
29
+ $finder = $this->buildFinder();
30
+ $finder->directories();
31
+ $finder->files();
32
+ $finder->directories();
33
+ $this->assertIterator($this->toAbsolute(array('foo', 'toto')), $finder->in(self::$tmpDir)->getIterator());
34
+ }
35
+
36
+ public function testFiles()
37
+ {
38
+ $finder = $this->buildFinder();
39
+ $this->assertSame($finder, $finder->files());
40
+ $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'test.py', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
41
+
42
+ $finder = $this->buildFinder();
43
+ $finder->files();
44
+ $finder->directories();
45
+ $finder->files();
46
+ $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'test.py', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
47
+ }
48
+
49
+ public function testDepth()
50
+ {
51
+ $finder = $this->buildFinder();
52
+ $this->assertSame($finder, $finder->depth('< 1'));
53
+ $this->assertIterator($this->toAbsolute(array('foo', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
54
+
55
+ $finder = $this->buildFinder();
56
+ $this->assertSame($finder, $finder->depth('<= 0'));
57
+ $this->assertIterator($this->toAbsolute(array('foo', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
58
+
59
+ $finder = $this->buildFinder();
60
+ $this->assertSame($finder, $finder->depth('>= 1'));
61
+ $this->assertIterator($this->toAbsolute(array('foo/bar.tmp')), $finder->in(self::$tmpDir)->getIterator());
62
+
63
+ $finder = $this->buildFinder();
64
+ $finder->depth('< 1')->depth('>= 1');
65
+ $this->assertIterator(array(), $finder->in(self::$tmpDir)->getIterator());
66
+ }
67
+
68
+ public function testName()
69
+ {
70
+ $finder = $this->buildFinder();
71
+ $this->assertSame($finder, $finder->name('*.php'));
72
+ $this->assertIterator($this->toAbsolute(array('test.php')), $finder->in(self::$tmpDir)->getIterator());
73
+
74
+ $finder = $this->buildFinder();
75
+ $finder->name('test.ph*');
76
+ $finder->name('test.py');
77
+ $this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator());
78
+
79
+ $finder = $this->buildFinder();
80
+ $finder->name('~^test~i');
81
+ $this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator());
82
+
83
+ $finder = $this->buildFinder();
84
+ $finder->name('~\\.php$~i');
85
+ $this->assertIterator($this->toAbsolute(array('test.php')), $finder->in(self::$tmpDir)->getIterator());
86
+
87
+ $finder = $this->buildFinder();
88
+ $finder->name('test.p{hp,y}');
89
+ $this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator());
90
+ }
91
+
92
+ public function testNotName()
93
+ {
94
+ $finder = $this->buildFinder();
95
+ $this->assertSame($finder, $finder->notName('*.php'));
96
+ $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
97
+
98
+ $finder = $this->buildFinder();
99
+ $finder->notName('*.php');
100
+ $finder->notName('*.py');
101
+ $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
102
+
103
+ $finder = $this->buildFinder();
104
+ $finder->name('test.ph*');
105
+ $finder->name('test.py');
106
+ $finder->notName('*.php');
107
+ $finder->notName('*.py');
108
+ $this->assertIterator(array(), $finder->in(self::$tmpDir)->getIterator());
109
+
110
+ $finder = $this->buildFinder();
111
+ $finder->name('test.ph*');
112
+ $finder->name('test.py');
113
+ $finder->notName('*.p{hp,y}');
114
+ $this->assertIterator(array(), $finder->in(self::$tmpDir)->getIterator());
115
+ }
116
+
117
+ /**
118
+ * @dataProvider getRegexNameTestData
119
+ */
120
+ public function testRegexName($regex)
121
+ {
122
+ $finder = $this->buildFinder();
123
+ $finder->name($regex);
124
+ $this->assertIterator($this->toAbsolute(array('test.py', 'test.php')), $finder->in(self::$tmpDir)->getIterator());
125
+ }
126
+
127
+ public function testSize()
128
+ {
129
+ $finder = $this->buildFinder();
130
+ $this->assertSame($finder, $finder->files()->size('< 1K')->size('> 500'));
131
+ $this->assertIterator($this->toAbsolute(array('test.php')), $finder->in(self::$tmpDir)->getIterator());
132
+ }
133
+
134
+ public function testDate()
135
+ {
136
+ $finder = $this->buildFinder();
137
+ $this->assertSame($finder, $finder->files()->date('until last month'));
138
+ $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php')), $finder->in(self::$tmpDir)->getIterator());
139
+ }
140
+
141
+ public function testExclude()
142
+ {
143
+ $finder = $this->buildFinder();
144
+ $this->assertSame($finder, $finder->exclude('foo'));
145
+ $this->assertIterator($this->toAbsolute(array('test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
146
+ }
147
+
148
+ public function testIgnoreVCS()
149
+ {
150
+ $finder = $this->buildFinder();
151
+ $this->assertSame($finder, $finder->ignoreVCS(false)->ignoreDotFiles(false));
152
+ $this->assertIterator($this->toAbsolute(array('.git', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'toto/.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
153
+
154
+ $finder = $this->buildFinder();
155
+ $finder->ignoreVCS(false)->ignoreVCS(false)->ignoreDotFiles(false);
156
+ $this->assertIterator($this->toAbsolute(array('.git', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'toto/.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
157
+
158
+ $finder = $this->buildFinder();
159
+ $this->assertSame($finder, $finder->ignoreVCS(true)->ignoreDotFiles(false));
160
+ $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
161
+ }
162
+
163
+ public function testIgnoreDotFiles()
164
+ {
165
+ $finder = $this->buildFinder();
166
+ $this->assertSame($finder, $finder->ignoreDotFiles(false)->ignoreVCS(false));
167
+ $this->assertIterator($this->toAbsolute(array('.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'toto/.git', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
168
+
169
+ $finder = $this->buildFinder();
170
+ $finder->ignoreDotFiles(false)->ignoreDotFiles(false)->ignoreVCS(false);
171
+ $this->assertIterator($this->toAbsolute(array('.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'toto/.git', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
172
+
173
+ $finder = $this->buildFinder();
174
+ $this->assertSame($finder, $finder->ignoreDotFiles(true)->ignoreVCS(false));
175
+ $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
176
+ }
177
+
178
+ public function testSortByName()
179
+ {
180
+ $finder = $this->buildFinder();
181
+ $this->assertSame($finder, $finder->sortByName());
182
+ $this->assertIterator($this->toAbsolute(array('foo', 'foo bar', 'foo/bar.tmp', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir)->getIterator());
183
+ }
184
+
185
+ public function testSortByType()
186
+ {
187
+ $finder = $this->buildFinder();
188
+ $this->assertSame($finder, $finder->sortByType());
189
+ $this->assertIterator($this->toAbsolute(array('foo', 'foo bar', 'toto', 'foo/bar.tmp', 'test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator());
190
+ }
191
+
192
+ public function testSortByAccessedTime()
193
+ {
194
+ $finder = $this->buildFinder();
195
+ $this->assertSame($finder, $finder->sortByAccessedTime());
196
+ $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'toto', 'test.py', 'foo', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
197
+ }
198
+
199
+ public function testSortByChangedTime()
200
+ {
201
+ $finder = $this->buildFinder();
202
+ $this->assertSame($finder, $finder->sortByChangedTime());
203
+ $this->assertIterator($this->toAbsolute(array('toto', 'test.py', 'test.php', 'foo/bar.tmp', 'foo', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
204
+ }
205
+
206
+ public function testSortByModifiedTime()
207
+ {
208
+ $finder = $this->buildFinder();
209
+ $this->assertSame($finder, $finder->sortByModifiedTime());
210
+ $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'toto', 'test.py', 'foo', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
211
+ }
212
+
213
+ public function testSort()
214
+ {
215
+ $finder = $this->buildFinder();
216
+ $this->assertSame($finder, $finder->sort(function (\SplFileInfo $a, \SplFileInfo $b) { return strcmp($a->getRealPath(), $b->getRealPath()); }));
217
+ $this->assertIterator($this->toAbsolute(array('foo', 'foo bar', 'foo/bar.tmp', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir)->getIterator());
218
+ }
219
+
220
+ public function testFilter()
221
+ {
222
+ $finder = $this->buildFinder();
223
+ $this->assertSame($finder, $finder->filter(function (\SplFileInfo $f) { return false !== strpos($f, 'test'); }));
224
+ $this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator());
225
+ }
226
+
227
+ public function testFollowLinks()
228
+ {
229
+ if ('\\' == DIRECTORY_SEPARATOR) {
230
+ $this->markTestSkipped('symlinks are not supported on Windows');
231
+ }
232
+
233
+ $finder = $this->buildFinder();
234
+ $this->assertSame($finder, $finder->followLinks());
235
+ $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator());
236
+ }
237
+
238
+ public function testIn()
239
+ {
240
+ $finder = $this->buildFinder();
241
+ $iterator = $finder->files()->name('*.php')->depth('< 1')->in(array(self::$tmpDir, __DIR__))->getIterator();
242
+
243
+ $expected = array(
244
+ self::$tmpDir.DIRECTORY_SEPARATOR.'test.php',
245
+ __DIR__.DIRECTORY_SEPARATOR.'FinderTest.php',
246
+ __DIR__.DIRECTORY_SEPARATOR.'GlobTest.php',
247
+ );
248
+
249
+ $this->assertIterator($expected, $iterator);
250
+ }
251
+
252
+ /**
253
+ * @expectedException \InvalidArgumentException
254
+ */
255
+ public function testInWithNonExistentDirectory()
256
+ {
257
+ $finder = new Finder();
258
+ $finder->in('foobar');
259
+ }
260
+
261
+ public function testInWithGlob()
262
+ {
263
+ $finder = $this->buildFinder();
264
+ $finder->in(array(__DIR__.'/Fixtures/*/B/C', __DIR__.'/Fixtures/*/*/B/C'))->getIterator();
265
+
266
+ $this->assertIterator($this->toAbsoluteFixtures(array('A/B/C/abc.dat', 'copy/A/B/C/abc.dat.copy')), $finder);
267
+ }
268
+
269
+ /**
270
+ * @expectedException \InvalidArgumentException
271
+ */
272
+ public function testInWithNonDirectoryGlob()
273
+ {
274
+ $finder = new Finder();
275
+ $finder->in(__DIR__.'/Fixtures/A/a*');
276
+ }
277
+
278
+ public function testInWithGlobBrace()
279
+ {
280
+ $finder = $this->buildFinder();
281
+ $finder->in(array(__DIR__.'/Fixtures/{A,copy/A}/B/C'))->getIterator();
282
+
283
+ $this->assertIterator($this->toAbsoluteFixtures(array('A/B/C/abc.dat', 'copy/A/B/C/abc.dat.copy')), $finder);
284
+ }
285
+
286
+ /**
287
+ * @expectedException \LogicException
288
+ */
289
+ public function testGetIteratorWithoutIn()
290
+ {
291
+ $finder = Finder::create();
292
+ $finder->getIterator();
293
+ }
294
+
295
+ public function testGetIterator()
296
+ {
297
+ $finder = $this->buildFinder();
298
+ $dirs = array();
299
+ foreach ($finder->directories()->in(self::$tmpDir) as $dir) {
300
+ $dirs[] = (string) $dir;
301
+ }
302
+
303
+ $expected = $this->toAbsolute(array('foo', 'toto'));
304
+
305
+ sort($dirs);
306
+ sort($expected);
307
+
308
+ $this->assertEquals($expected, $dirs, 'implements the \IteratorAggregate interface');
309
+
310
+ $finder = $this->buildFinder();
311
+ $this->assertEquals(2, iterator_count($finder->directories()->in(self::$tmpDir)), 'implements the \IteratorAggregate interface');
312
+
313
+ $finder = $this->buildFinder();
314
+ $a = iterator_to_array($finder->directories()->in(self::$tmpDir));
315
+ $a = array_values(array_map('strval', $a));
316
+ sort($a);
317
+ $this->assertEquals($expected, $a, 'implements the \IteratorAggregate interface');
318
+ }
319
+
320
+ public function testRelativePath()
321
+ {
322
+ $finder = $this->buildFinder()->in(self::$tmpDir);
323
+
324
+ $paths = array();
325
+
326
+ foreach ($finder as $file) {
327
+ $paths[] = $file->getRelativePath();
328
+ }
329
+
330
+ $ref = array('', '', '', '', 'foo', '');
331
+
332
+ sort($ref);
333
+ sort($paths);
334
+
335
+ $this->assertEquals($ref, $paths);
336
+ }
337
+
338
+ public function testRelativePathname()
339
+ {
340
+ $finder = $this->buildFinder()->in(self::$tmpDir)->sortByName();
341
+
342
+ $paths = array();
343
+
344
+ foreach ($finder as $file) {
345
+ $paths[] = $file->getRelativePathname();
346
+ }
347
+
348
+ $ref = array('test.php', 'toto', 'test.py', 'foo', 'foo'.DIRECTORY_SEPARATOR.'bar.tmp', 'foo bar');
349
+
350
+ sort($paths);
351
+ sort($ref);
352
+
353
+ $this->assertEquals($ref, $paths);
354
+ }
355
+
356
+ public function testAppendWithAFinder()
357
+ {
358
+ $finder = $this->buildFinder();
359
+ $finder->files()->in(self::$tmpDir.DIRECTORY_SEPARATOR.'foo');
360
+
361
+ $finder1 = $this->buildFinder();
362
+ $finder1->directories()->in(self::$tmpDir);
363
+
364
+ $finder = $finder->append($finder1);
365
+
366
+ $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'toto')), $finder->getIterator());
367
+ }
368
+
369
+ public function testAppendWithAnArray()
370
+ {
371
+ $finder = $this->buildFinder();
372
+ $finder->files()->in(self::$tmpDir.DIRECTORY_SEPARATOR.'foo');
373
+
374
+ $finder->append($this->toAbsolute(array('foo', 'toto')));
375
+
376
+ $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'toto')), $finder->getIterator());
377
+ }
378
+
379
+ public function testAppendReturnsAFinder()
380
+ {
381
+ $this->assertInstanceOf('Symfony\\Component\\Finder\\Finder', Finder::create()->append(array()));
382
+ }
383
+
384
+ public function testAppendDoesNotRequireIn()
385
+ {
386
+ $finder = $this->buildFinder();
387
+ $finder->in(self::$tmpDir.DIRECTORY_SEPARATOR.'foo');
388
+
389
+ $finder1 = Finder::create()->append($finder);
390
+
391
+ $this->assertIterator(iterator_to_array($finder->getIterator()), $finder1->getIterator());
392
+ }
393
+
394
+ public function testCountDirectories()
395
+ {
396
+ $directory = Finder::create()->directories()->in(self::$tmpDir);
397
+ $i = 0;
398
+
399
+ foreach ($directory as $dir) {
400
+ ++$i;
401
+ }
402
+
403
+ $this->assertCount($i, $directory);
404
+ }
405
+
406
+ public function testCountFiles()
407
+ {
408
+ $files = Finder::create()->files()->in(__DIR__.DIRECTORY_SEPARATOR.'Fixtures');
409
+ $i = 0;
410
+
411
+ foreach ($files as $file) {
412
+ ++$i;
413
+ }
414
+
415
+ $this->assertCount($i, $files);
416
+ }
417
+
418
+ /**
419
+ * @expectedException \LogicException
420
+ */
421
+ public function testCountWithoutIn()
422
+ {
423
+ $finder = Finder::create()->files();
424
+ count($finder);
425
+ }
426
+
427
+ public function testHasResults()
428
+ {
429
+ $finder = $this->buildFinder();
430
+ $finder->in(__DIR__);
431
+ $this->assertTrue($finder->hasResults());
432
+ }
433
+
434
+ public function testNoResults()
435
+ {
436
+ $finder = $this->buildFinder();
437
+ $finder->in(__DIR__)->name('DoesNotExist');
438
+ $this->assertFalse($finder->hasResults());
439
+ }
440
+
441
+ /**
442
+ * @dataProvider getContainsTestData
443
+ */
444
+ public function testContains($matchPatterns, $noMatchPatterns, $expected)
445
+ {
446
+ $finder = $this->buildFinder();
447
+ $finder->in(__DIR__.DIRECTORY_SEPARATOR.'Fixtures')
448
+ ->name('*.txt')->sortByName()
449
+ ->contains($matchPatterns)
450
+ ->notContains($noMatchPatterns);
451
+
452
+ $this->assertIterator($this->toAbsoluteFixtures($expected), $finder);
453
+ }
454
+
455
+ public function testContainsOnDirectory()
456
+ {
457
+ $finder = $this->buildFinder();
458
+ $finder->in(__DIR__)
459
+ ->directories()
460
+ ->name('Fixtures')
461
+ ->contains('abc');
462
+ $this->assertIterator(array(), $finder);
463
+ }
464
+
465
+ public function testNotContainsOnDirectory()
466
+ {
467
+ $finder = $this->buildFinder();
468
+ $finder->in(__DIR__)
469
+ ->directories()
470
+ ->name('Fixtures')
471
+ ->notContains('abc');
472
+ $this->assertIterator(array(), $finder);
473
+ }
474
+
475
+ /**
476
+ * Searching in multiple locations involves AppendIterator which does an unnecessary rewind which leaves FilterIterator
477
+ * with inner FilesystemIterator in an invalid state.
478
+ *
479
+ * @see https://bugs.php.net/68557
480
+ */
481
+ public function testMultipleLocations()
482
+ {
483
+ $locations = array(
484
+ self::$tmpDir.'/',
485
+ self::$tmpDir.'/toto/',
486
+ );
487
+
488
+ // it is expected that there are test.py test.php in the tmpDir
489
+ $finder = new Finder();
490
+ $finder->in($locations)
491
+ // the default flag IGNORE_DOT_FILES fixes the problem indirectly
492
+ // so we set it to false for better isolation
493
+ ->ignoreDotFiles(false)
494
+ ->depth('< 1')->name('test.php');
495
+
496
+ $this->assertCount(1, $finder);
497
+ }
498
+
499
+ /**
500
+ * Searching in multiple locations with sub directories involves
501
+ * AppendIterator which does an unnecessary rewind which leaves
502
+ * FilterIterator with inner FilesystemIterator in an invalid state.
503
+ *
504
+ * @see https://bugs.php.net/68557
505
+ */
506
+ public function testMultipleLocationsWithSubDirectories()
507
+ {
508
+ $locations = array(
509
+ __DIR__.'/Fixtures/one',
510
+ self::$tmpDir.DIRECTORY_SEPARATOR.'toto',
511
+ );
512
+
513
+ $finder = $this->buildFinder();
514
+ $finder->in($locations)->depth('< 10')->name('*.neon');
515
+
516
+ $expected = array(
517
+ __DIR__.'/Fixtures/one'.DIRECTORY_SEPARATOR.'b'.DIRECTORY_SEPARATOR.'c.neon',
518
+ __DIR__.'/Fixtures/one'.DIRECTORY_SEPARATOR.'b'.DIRECTORY_SEPARATOR.'d.neon',
519
+ );
520
+
521
+ $this->assertIterator($expected, $finder);
522
+ $this->assertIteratorInForeach($expected, $finder);
523
+ }
524
+
525
+ /**
526
+ * Iterator keys must be the file pathname.
527
+ */
528
+ public function testIteratorKeys()
529
+ {
530
+ $finder = $this->buildFinder()->in(self::$tmpDir);
531
+ foreach ($finder as $key => $file) {
532
+ $this->assertEquals($file->getPathname(), $key);
533
+ }
534
+ }
535
+
536
+ public function testRegexSpecialCharsLocationWithPathRestrictionContainingStartFlag()
537
+ {
538
+ $finder = $this->buildFinder();
539
+ $finder->in(__DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'r+e.gex[c]a(r)s')
540
+ ->path('/^dir/');
541
+
542
+ $expected = array('r+e.gex[c]a(r)s'.DIRECTORY_SEPARATOR.'dir', 'r+e.gex[c]a(r)s'.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'bar.dat');
543
+ $this->assertIterator($this->toAbsoluteFixtures($expected), $finder);
544
+ }
545
+
546
+ public function getContainsTestData()
547
+ {
548
+ return array(
549
+ array('', '', array()),
550
+ array('foo', 'bar', array()),
551
+ array('', 'foobar', array('dolor.txt', 'ipsum.txt', 'lorem.txt')),
552
+ array('lorem ipsum dolor sit amet', 'foobar', array('lorem.txt')),
553
+ array('sit', 'bar', array('dolor.txt', 'ipsum.txt', 'lorem.txt')),
554
+ array('dolor sit amet', '@^L@m', array('dolor.txt', 'ipsum.txt')),
555
+ array('/^lorem ipsum dolor sit amet$/m', 'foobar', array('lorem.txt')),
556
+ array('lorem', 'foobar', array('lorem.txt')),
557
+ array('', 'lorem', array('dolor.txt', 'ipsum.txt')),
558
+ array('ipsum dolor sit amet', '/^IPSUM/m', array('lorem.txt')),
559
+ );
560
+ }
561
+
562
+ public function getRegexNameTestData()
563
+ {
564
+ return array(
565
+ array('~.+\\.p.+~i'),
566
+ array('~t.*s~i'),
567
+ );
568
+ }
569
+
570
+ /**
571
+ * @dataProvider getTestPathData
572
+ */
573
+ public function testPath($matchPatterns, $noMatchPatterns, array $expected)
574
+ {
575
+ $finder = $this->buildFinder();
576
+ $finder->in(__DIR__.DIRECTORY_SEPARATOR.'Fixtures')
577
+ ->path($matchPatterns)
578
+ ->notPath($noMatchPatterns);
579
+
580
+ $this->assertIterator($this->toAbsoluteFixtures($expected), $finder);
581
+ }
582
+
583
+ public function getTestPathData()
584
+ {
585
+ return array(
586
+ array('', '', array()),
587
+ array('/^A\/B\/C/', '/C$/',
588
+ array('A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat'),
589
+ ),
590
+ array('/^A\/B/', 'foobar',
591
+ array(
592
+ 'A'.DIRECTORY_SEPARATOR.'B',
593
+ 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C',
594
+ 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat',
595
+ 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat',
596
+ ),
597
+ ),
598
+ array('A/B/C', 'foobar',
599
+ array(
600
+ 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C',
601
+ 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat',
602
+ 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C',
603
+ 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat.copy',
604
+ ),
605
+ ),
606
+ array('A/B', 'foobar',
607
+ array(
608
+ //dirs
609
+ 'A'.DIRECTORY_SEPARATOR.'B',
610
+ 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C',
611
+ 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B',
612
+ 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C',
613
+ //files
614
+ 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat',
615
+ 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat',
616
+ 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat.copy',
617
+ 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat.copy',
618
+ ),
619
+ ),
620
+ array('/^with space\//', 'foobar',
621
+ array(
622
+ 'with space'.DIRECTORY_SEPARATOR.'foo.txt',
623
+ ),
624
+ ),
625
+ );
626
+ }
627
+
628
+ public function testAccessDeniedException()
629
+ {
630
+ if ('\\' === DIRECTORY_SEPARATOR) {
631
+ $this->markTestSkipped('chmod is not supported on Windows');
632
+ }
633
+
634
+ $finder = $this->buildFinder();
635
+ $finder->files()->in(self::$tmpDir);
636
+
637
+ // make 'foo' directory non-readable
638
+ $testDir = self::$tmpDir.DIRECTORY_SEPARATOR.'foo';
639
+ chmod($testDir, 0333);
640
+
641
+ if (false === $couldRead = is_readable($testDir)) {
642
+ try {
643
+ $this->assertIterator($this->toAbsolute(array('foo bar', 'test.php', 'test.py')), $finder->getIterator());
644
+ $this->fail('Finder should throw an exception when opening a non-readable directory.');
645
+ } catch (\Exception $e) {
646
+ $expectedExceptionClass = 'Symfony\\Component\\Finder\\Exception\\AccessDeniedException';
647
+ if ($e instanceof \PHPUnit_Framework_ExpectationFailedException) {
648
+ $this->fail(sprintf("Expected exception:\n%s\nGot:\n%s\nWith comparison failure:\n%s", $expectedExceptionClass, 'PHPUnit_Framework_ExpectationFailedException', $e->getComparisonFailure()->getExpectedAsString()));
649
+ }
650
+
651
+ if ($e instanceof \PHPUnit\Framework\ExpectationFailedException) {
652
+ $this->fail(sprintf("Expected exception:\n%s\nGot:\n%s\nWith comparison failure:\n%s", $expectedExceptionClass, '\PHPUnit\Framework\ExpectationFailedException', $e->getComparisonFailure()->getExpectedAsString()));
653
+ }
654
+
655
+ $this->assertInstanceOf($expectedExceptionClass, $e);
656
+ }
657
+ }
658
+
659
+ // restore original permissions
660
+ chmod($testDir, 0777);
661
+ clearstatcache($testDir);
662
+
663
+ if ($couldRead) {
664
+ $this->markTestSkipped('could read test files while test requires unreadable');
665
+ }
666
+ }
667
+
668
+ public function testIgnoredAccessDeniedException()
669
+ {
670
+ if ('\\' === DIRECTORY_SEPARATOR) {
671
+ $this->markTestSkipped('chmod is not supported on Windows');
672
+ }
673
+
674
+ $finder = $this->buildFinder();
675
+ $finder->files()->ignoreUnreadableDirs()->in(self::$tmpDir);
676
+
677
+ // make 'foo' directory non-readable
678
+ $testDir = self::$tmpDir.DIRECTORY_SEPARATOR.'foo';
679
+ chmod($testDir, 0333);
680
+
681
+ if (false === ($couldRead = is_readable($testDir))) {
682
+ $this->assertIterator($this->toAbsolute(array('foo bar', 'test.php', 'test.py')), $finder->getIterator());
683
+ }
684
+
685
+ // restore original permissions
686
+ chmod($testDir, 0777);
687
+ clearstatcache($testDir);
688
+
689
+ if ($couldRead) {
690
+ $this->markTestSkipped('could read test files while test requires unreadable');
691
+ }
692
+ }
693
+
694
+ protected function buildFinder()
695
+ {
696
+ return Finder::create();
697
+ }
698
+ }
vendor/symfony/finder/Tests/Fixtures/.dot/a ADDED
File without changes
vendor/symfony/finder/Tests/Fixtures/.dot/b/c.neon ADDED
File without changes
vendor/symfony/finder/Tests/Fixtures/.dot/b/d.neon ADDED
File without changes
vendor/symfony/finder/Tests/Fixtures/A/B/C/abc.dat ADDED
File without changes
vendor/symfony/finder/Tests/Fixtures/A/B/ab.dat ADDED
File without changes
vendor/symfony/finder/Tests/Fixtures/A/a.dat ADDED
File without changes
vendor/symfony/finder/Tests/Fixtures/copy/A/B/C/abc.dat.copy ADDED
File without changes
vendor/symfony/finder/Tests/Fixtures/copy/A/B/ab.dat.copy ADDED
File without changes
vendor/symfony/finder/Tests/Fixtures/copy/A/a.dat.copy ADDED
File without changes
vendor/symfony/finder/Tests/Fixtures/dolor.txt ADDED
@@ -0,0 +1,2 @@
 
 
1
+ dolor sit amet
2
+ DOLOR SIT AMET
vendor/symfony/finder/Tests/Fixtures/ipsum.txt ADDED
@@ -0,0 +1,2 @@
 
 
1
+ ipsum dolor sit amet
2
+ IPSUM DOLOR SIT AMET
vendor/symfony/finder/Tests/Fixtures/lorem.txt ADDED
@@ -0,0 +1,2 @@
 
 
1
+ lorem ipsum dolor sit amet
2
+ LOREM IPSUM DOLOR SIT AMET
vendor/symfony/finder/Tests/Fixtures/one/.dot ADDED
@@ -0,0 +1 @@
 
1
+ .dot
vendor/symfony/finder/Tests/Fixtures/one/a ADDED
File without changes
vendor/symfony/finder/Tests/Fixtures/one/b/c.neon ADDED
File without changes
vendor/symfony/finder/Tests/Fixtures/one/b/d.neon ADDED
File without changes
vendor/symfony/finder/Tests/Fixtures/r+e.gex[c]a(r)s/dir/bar.dat ADDED
File without changes
vendor/symfony/finder/Tests/Fixtures/with space/foo.txt ADDED
File without changes
vendor/symfony/finder/Tests/GlobTest.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests;
13
+
14
+ use PHPUnit\Framework\TestCase;
15
+ use Symfony\Component\Finder\Finder;
16
+ use Symfony\Component\Finder\Glob;
17
+
18
+ class GlobTest extends TestCase
19
+ {
20
+ public function testGlobToRegexDelimiters()
21
+ {
22
+ $this->assertEquals('#^(?=[^\.])\#$#', Glob::toRegex('#'));
23
+ $this->assertEquals('#^\.[^/]*$#', Glob::toRegex('.*'));
24
+ $this->assertEquals('^\.[^/]*$', Glob::toRegex('.*', true, true, ''));
25
+ $this->assertEquals('/^\.[^/]*$/', Glob::toRegex('.*', true, true, '/'));
26
+ }
27
+
28
+ public function testGlobToRegexDoubleStarStrictDots()
29
+ {
30
+ $finder = new Finder();
31
+ $finder->ignoreDotFiles(false);
32
+ $regex = Glob::toRegex('/**/*.neon');
33
+
34
+ foreach ($finder->in(__DIR__) as $k => $v) {
35
+ $k = str_replace(DIRECTORY_SEPARATOR, '/', $k);
36
+ if (preg_match($regex, substr($k, strlen(__DIR__)))) {
37
+ $match[] = substr($k, 10 + strlen(__DIR__));
38
+ }
39
+ }
40
+ sort($match);
41
+
42
+ $this->assertSame(array('one/b/c.neon', 'one/b/d.neon'), $match);
43
+ }
44
+
45
+ public function testGlobToRegexDoubleStarNonStrictDots()
46
+ {
47
+ $finder = new Finder();
48
+ $finder->ignoreDotFiles(false);
49
+ $regex = Glob::toRegex('/**/*.neon', false);
50
+
51
+ foreach ($finder->in(__DIR__) as $k => $v) {
52
+ $k = str_replace(DIRECTORY_SEPARATOR, '/', $k);
53
+ if (preg_match($regex, substr($k, strlen(__DIR__)))) {
54
+ $match[] = substr($k, 10 + strlen(__DIR__));
55
+ }
56
+ }
57
+ sort($match);
58
+
59
+ $this->assertSame(array('.dot/b/c.neon', '.dot/b/d.neon', 'one/b/c.neon', 'one/b/d.neon'), $match);
60
+ }
61
+
62
+ public function testGlobToRegexDoubleStarWithoutLeadingSlash()
63
+ {
64
+ $finder = new Finder();
65
+ $finder->ignoreDotFiles(false);
66
+ $regex = Glob::toRegex('/Fixtures/one/**');
67
+
68
+ foreach ($finder->in(__DIR__) as $k => $v) {
69
+ $k = str_replace(DIRECTORY_SEPARATOR, '/', $k);
70
+ if (preg_match($regex, substr($k, strlen(__DIR__)))) {
71
+ $match[] = substr($k, 10 + strlen(__DIR__));
72
+ }
73
+ }
74
+ sort($match);
75
+
76
+ $this->assertSame(array('one/a', 'one/b', 'one/b/c.neon', 'one/b/d.neon'), $match);
77
+ }
78
+
79
+ public function testGlobToRegexDoubleStarWithoutLeadingSlashNotStrictLeadingDot()
80
+ {
81
+ $finder = new Finder();
82
+ $finder->ignoreDotFiles(false);
83
+ $regex = Glob::toRegex('/Fixtures/one/**', false);
84
+
85
+ foreach ($finder->in(__DIR__) as $k => $v) {
86
+ $k = str_replace(DIRECTORY_SEPARATOR, '/', $k);
87
+ if (preg_match($regex, substr($k, strlen(__DIR__)))) {
88
+ $match[] = substr($k, 10 + strlen(__DIR__));
89
+ }
90
+ }
91
+ sort($match);
92
+
93
+ $this->assertSame(array('one/.dot', 'one/a', 'one/b', 'one/b/c.neon', 'one/b/d.neon'), $match);
94
+ }
95
+ }
vendor/symfony/finder/Tests/Iterator/CustomFilterIteratorTest.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ use Symfony\Component\Finder\Iterator\CustomFilterIterator;
15
+
16
+ class CustomFilterIteratorTest extends IteratorTestCase
17
+ {
18
+ /**
19
+ * @expectedException \InvalidArgumentException
20
+ */
21
+ public function testWithInvalidFilter()
22
+ {
23
+ new CustomFilterIterator(new Iterator(), array('foo'));
24
+ }
25
+
26
+ /**
27
+ * @dataProvider getAcceptData
28
+ */
29
+ public function testAccept($filters, $expected)
30
+ {
31
+ $inner = new Iterator(array('test.php', 'test.py', 'foo.php'));
32
+
33
+ $iterator = new CustomFilterIterator($inner, $filters);
34
+
35
+ $this->assertIterator($expected, $iterator);
36
+ }
37
+
38
+ public function getAcceptData()
39
+ {
40
+ return array(
41
+ array(array(function (\SplFileInfo $fileinfo) { return false; }), array()),
42
+ array(array(function (\SplFileInfo $fileinfo) { return 0 === strpos($fileinfo, 'test'); }), array('test.php', 'test.py')),
43
+ array(array('is_dir'), array()),
44
+ );
45
+ }
46
+ }
vendor/symfony/finder/Tests/Iterator/DateRangeFilterIteratorTest.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ use Symfony\Component\Finder\Iterator\DateRangeFilterIterator;
15
+ use Symfony\Component\Finder\Comparator\DateComparator;
16
+
17
+ class DateRangeFilterIteratorTest extends RealIteratorTestCase
18
+ {
19
+ /**
20
+ * @dataProvider getAcceptData
21
+ */
22
+ public function testAccept($size, $expected)
23
+ {
24
+ $files = self::$files;
25
+ $files[] = self::toAbsolute('doesnotexist');
26
+ $inner = new Iterator($files);
27
+
28
+ $iterator = new DateRangeFilterIterator($inner, $size);
29
+
30
+ $this->assertIterator($expected, $iterator);
31
+ }
32
+
33
+ public function getAcceptData()
34
+ {
35
+ $since20YearsAgo = array(
36
+ '.git',
37
+ 'test.py',
38
+ 'foo',
39
+ 'foo/bar.tmp',
40
+ 'test.php',
41
+ 'toto',
42
+ 'toto/.git',
43
+ '.bar',
44
+ '.foo',
45
+ '.foo/.bar',
46
+ 'foo bar',
47
+ '.foo/bar',
48
+ );
49
+
50
+ $since2MonthsAgo = array(
51
+ '.git',
52
+ 'test.py',
53
+ 'foo',
54
+ 'toto',
55
+ 'toto/.git',
56
+ '.bar',
57
+ '.foo',
58
+ '.foo/.bar',
59
+ 'foo bar',
60
+ '.foo/bar',
61
+ );
62
+
63
+ $untilLastMonth = array(
64
+ 'foo/bar.tmp',
65
+ 'test.php',
66
+ );
67
+
68
+ return array(
69
+ array(array(new DateComparator('since 20 years ago')), $this->toAbsolute($since20YearsAgo)),
70
+ array(array(new DateComparator('since 2 months ago')), $this->toAbsolute($since2MonthsAgo)),
71
+ array(array(new DateComparator('until last month')), $this->toAbsolute($untilLastMonth)),
72
+ );
73
+ }
74
+ }
vendor/symfony/finder/Tests/Iterator/DepthRangeFilterIteratorTest.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator;
15
+
16
+ class DepthRangeFilterIteratorTest extends RealIteratorTestCase
17
+ {
18
+ /**
19
+ * @dataProvider getAcceptData
20
+ */
21
+ public function testAccept($minDepth, $maxDepth, $expected)
22
+ {
23
+ $inner = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->toAbsolute(), \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST);
24
+
25
+ $iterator = new DepthRangeFilterIterator($inner, $minDepth, $maxDepth);
26
+
27
+ $actual = array_keys(iterator_to_array($iterator));
28
+ sort($expected);
29
+ sort($actual);
30
+ $this->assertEquals($expected, $actual);
31
+ }
32
+
33
+ public function getAcceptData()
34
+ {
35
+ $lessThan1 = array(
36
+ '.git',
37
+ 'test.py',
38
+ 'foo',
39
+ 'test.php',
40
+ 'toto',
41
+ '.foo',
42
+ '.bar',
43
+ 'foo bar',
44
+ );
45
+
46
+ $lessThanOrEqualTo1 = array(
47
+ '.git',
48
+ 'test.py',
49
+ 'foo',
50
+ 'foo/bar.tmp',
51
+ 'test.php',
52
+ 'toto',
53
+ 'toto/.git',
54
+ '.foo',
55
+ '.foo/.bar',
56
+ '.bar',
57
+ 'foo bar',
58
+ '.foo/bar',
59
+ );
60
+
61
+ $graterThanOrEqualTo1 = array(
62
+ 'toto/.git',
63
+ 'foo/bar.tmp',
64
+ '.foo/.bar',
65
+ '.foo/bar',
66
+ );
67
+
68
+ $equalTo1 = array(
69
+ 'toto/.git',
70
+ 'foo/bar.tmp',
71
+ '.foo/.bar',
72
+ '.foo/bar',
73
+ );
74
+
75
+ return array(
76
+ array(0, 0, $this->toAbsolute($lessThan1)),
77
+ array(0, 1, $this->toAbsolute($lessThanOrEqualTo1)),
78
+ array(2, PHP_INT_MAX, array()),
79
+ array(1, PHP_INT_MAX, $this->toAbsolute($graterThanOrEqualTo1)),
80
+ array(1, 1, $this->toAbsolute($equalTo1)),
81
+ );
82
+ }
83
+ }
vendor/symfony/finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator;
15
+ use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator;
16
+
17
+ class ExcludeDirectoryFilterIteratorTest extends RealIteratorTestCase
18
+ {
19
+ /**
20
+ * @dataProvider getAcceptData
21
+ */
22
+ public function testAccept($directories, $expected)
23
+ {
24
+ $inner = new \RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->toAbsolute(), \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST);
25
+
26
+ $iterator = new ExcludeDirectoryFilterIterator($inner, $directories);
27
+
28
+ $this->assertIterator($expected, $iterator);
29
+ }
30
+
31
+ public function getAcceptData()
32
+ {
33
+ $foo = array(
34
+ '.bar',
35
+ '.foo',
36
+ '.foo/.bar',
37
+ '.foo/bar',
38
+ '.git',
39
+ 'test.py',
40
+ 'test.php',
41
+ 'toto',
42
+ 'toto/.git',
43
+ 'foo bar',
44
+ );
45
+
46
+ $fo = array(
47
+ '.bar',
48
+ '.foo',
49
+ '.foo/.bar',
50
+ '.foo/bar',
51
+ '.git',
52
+ 'test.py',
53
+ 'foo',
54
+ 'foo/bar.tmp',
55
+ 'test.php',
56
+ 'toto',
57
+ 'toto/.git',
58
+ 'foo bar',
59
+ );
60
+
61
+ $toto = array(
62
+ '.bar',
63
+ '.foo',
64
+ '.foo/.bar',
65
+ '.foo/bar',
66
+ '.git',
67
+ 'test.py',
68
+ 'foo',
69
+ 'foo/bar.tmp',
70
+ 'test.php',
71
+ 'foo bar',
72
+ );
73
+
74
+ return array(
75
+ array(array('foo'), $this->toAbsolute($foo)),
76
+ array(array('fo'), $this->toAbsolute($fo)),
77
+ array(array('toto/'), $this->toAbsolute($toto)),
78
+ );
79
+ }
80
+ }
vendor/symfony/finder/Tests/Iterator/FileTypeFilterIteratorTest.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ use Symfony\Component\Finder\Iterator\FileTypeFilterIterator;
15
+
16
+ class FileTypeFilterIteratorTest extends RealIteratorTestCase
17
+ {
18
+ /**
19
+ * @dataProvider getAcceptData
20
+ */
21
+ public function testAccept($mode, $expected)
22
+ {
23
+ $inner = new InnerTypeIterator(self::$files);
24
+
25
+ $iterator = new FileTypeFilterIterator($inner, $mode);
26
+
27
+ $this->assertIterator($expected, $iterator);
28
+ }
29
+
30
+ public function getAcceptData()
31
+ {
32
+ $onlyFiles = array(
33
+ 'test.py',
34
+ 'foo/bar.tmp',
35
+ 'test.php',
36
+ '.bar',
37
+ '.foo/.bar',
38
+ '.foo/bar',
39
+ 'foo bar',
40
+ );
41
+
42
+ $onlyDirectories = array(
43
+ '.git',
44
+ 'foo',
45
+ 'toto',
46
+ 'toto/.git',
47
+ '.foo',
48
+ );
49
+
50
+ return array(
51
+ array(FileTypeFilterIterator::ONLY_FILES, $this->toAbsolute($onlyFiles)),
52
+ array(FileTypeFilterIterator::ONLY_DIRECTORIES, $this->toAbsolute($onlyDirectories)),
53
+ );
54
+ }
55
+ }
56
+
57
+ class InnerTypeIterator extends \ArrayIterator
58
+ {
59
+ public function current()
60
+ {
61
+ return new \SplFileInfo(parent::current());
62
+ }
63
+
64
+ public function isFile()
65
+ {
66
+ return $this->current()->isFile();
67
+ }
68
+
69
+ public function isDir()
70
+ {
71
+ return $this->current()->isDir();
72
+ }
73
+ }
vendor/symfony/finder/Tests/Iterator/FilecontentFilterIteratorTest.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ use Symfony\Component\Finder\Iterator\FilecontentFilterIterator;
15
+
16
+ class FilecontentFilterIteratorTest extends IteratorTestCase
17
+ {
18
+ public function testAccept()
19
+ {
20
+ $inner = new MockFileListIterator(array('test.txt'));
21
+ $iterator = new FilecontentFilterIterator($inner, array(), array());
22
+ $this->assertIterator(array('test.txt'), $iterator);
23
+ }
24
+
25
+ public function testDirectory()
26
+ {
27
+ $inner = new MockFileListIterator(array('directory'));
28
+ $iterator = new FilecontentFilterIterator($inner, array('directory'), array());
29
+ $this->assertIterator(array(), $iterator);
30
+ }
31
+
32
+ public function testUnreadableFile()
33
+ {
34
+ $inner = new MockFileListIterator(array('file r-'));
35
+ $iterator = new FilecontentFilterIterator($inner, array('file r-'), array());
36
+ $this->assertIterator(array(), $iterator);
37
+ }
38
+
39
+ /**
40
+ * @dataProvider getTestFilterData
41
+ */
42
+ public function testFilter(\Iterator $inner, array $matchPatterns, array $noMatchPatterns, array $resultArray)
43
+ {
44
+ $iterator = new FilecontentFilterIterator($inner, $matchPatterns, $noMatchPatterns);
45
+ $this->assertIterator($resultArray, $iterator);
46
+ }
47
+
48
+ public function getTestFilterData()
49
+ {
50
+ $inner = new MockFileListIterator();
51
+
52
+ $inner[] = new MockSplFileInfo(array(
53
+ 'name' => 'a.txt',
54
+ 'contents' => 'Lorem ipsum...',
55
+ 'type' => 'file',
56
+ 'mode' => 'r+', )
57
+ );
58
+
59
+ $inner[] = new MockSplFileInfo(array(
60
+ 'name' => 'b.yml',
61
+ 'contents' => 'dolor sit...',
62
+ 'type' => 'file',
63
+ 'mode' => 'r+', )
64
+ );
65
+
66
+ $inner[] = new MockSplFileInfo(array(
67
+ 'name' => 'some/other/dir/third.php',
68
+ 'contents' => 'amet...',
69
+ 'type' => 'file',
70
+ 'mode' => 'r+', )
71
+ );
72
+
73
+ $inner[] = new MockSplFileInfo(array(
74
+ 'name' => 'unreadable-file.txt',
75
+ 'contents' => false,
76
+ 'type' => 'file',
77
+ 'mode' => 'r+', )
78
+ );
79
+
80
+ return array(
81
+ array($inner, array('.'), array(), array('a.txt', 'b.yml', 'some/other/dir/third.php')),
82
+ array($inner, array('ipsum'), array(), array('a.txt')),
83
+ array($inner, array('i', 'amet'), array('Lorem', 'amet'), array('b.yml')),
84
+ );
85
+ }
86
+ }
vendor/symfony/finder/Tests/Iterator/FilenameFilterIteratorTest.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ use Symfony\Component\Finder\Iterator\FilenameFilterIterator;
15
+
16
+ class FilenameFilterIteratorTest extends IteratorTestCase
17
+ {
18
+ /**
19
+ * @dataProvider getAcceptData
20
+ */
21
+ public function testAccept($matchPatterns, $noMatchPatterns, $expected)
22
+ {
23
+ $inner = new InnerNameIterator(array('test.php', 'test.py', 'foo.php'));
24
+
25
+ $iterator = new FilenameFilterIterator($inner, $matchPatterns, $noMatchPatterns);
26
+
27
+ $this->assertIterator($expected, $iterator);
28
+ }
29
+
30
+ public function getAcceptData()
31
+ {
32
+ return array(
33
+ array(array('test.*'), array(), array('test.php', 'test.py')),
34
+ array(array(), array('test.*'), array('foo.php')),
35
+ array(array('*.php'), array('test.*'), array('foo.php')),
36
+ array(array('*.php', '*.py'), array('foo.*'), array('test.php', 'test.py')),
37
+ array(array('/\.php$/'), array(), array('test.php', 'foo.php')),
38
+ array(array(), array('/\.php$/'), array('test.py')),
39
+ );
40
+ }
41
+ }
42
+
43
+ class InnerNameIterator extends \ArrayIterator
44
+ {
45
+ public function current()
46
+ {
47
+ return new \SplFileInfo(parent::current());
48
+ }
49
+
50
+ public function getFilename()
51
+ {
52
+ return parent::current();
53
+ }
54
+ }
vendor/symfony/finder/Tests/Iterator/FilterIteratorTest.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ /**
15
+ * @author Alex Bogomazov
16
+ *
17
+ * @group legacy
18
+ */
19
+ class FilterIteratorTest extends RealIteratorTestCase
20
+ {
21
+ public function testFilterFilesystemIterators()
22
+ {
23
+ $i = new \FilesystemIterator($this->toAbsolute());
24
+
25
+ // it is expected that there are test.py test.php in the tmpDir
26
+ $i = $this->getMockForAbstractClass('Symfony\Component\Finder\Iterator\FilterIterator', array($i));
27
+ $i->expects($this->any())
28
+ ->method('accept')
29
+ ->will($this->returnCallback(function () use ($i) {
30
+ return (bool) preg_match('/\.php/', (string) $i->current());
31
+ })
32
+ );
33
+
34
+ $c = 0;
35
+ foreach ($i as $item) {
36
+ ++$c;
37
+ }
38
+
39
+ $this->assertEquals(1, $c);
40
+
41
+ $i->rewind();
42
+
43
+ $c = 0;
44
+ foreach ($i as $item) {
45
+ ++$c;
46
+ }
47
+
48
+ // This would fail in php older than 5.5.23/5.6.7 with \FilterIterator
49
+ // but works with Symfony\Component\Finder\Iterator\FilterIterator
50
+ // see https://bugs.php.net/68557
51
+ $this->assertEquals(1, $c);
52
+ }
53
+ }
vendor/symfony/finder/Tests/Iterator/Iterator.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ class Iterator implements \Iterator
15
+ {
16
+ protected $values = array();
17
+
18
+ public function __construct(array $values = array())
19
+ {
20
+ foreach ($values as $value) {
21
+ $this->attach(new \SplFileInfo($value));
22
+ }
23
+ $this->rewind();
24
+ }
25
+
26
+ public function attach(\SplFileInfo $fileinfo)
27
+ {
28
+ $this->values[] = $fileinfo;
29
+ }
30
+
31
+ public function rewind()
32
+ {
33
+ reset($this->values);
34
+ }
35
+
36
+ public function valid()
37
+ {
38
+ return false !== $this->current();
39
+ }
40
+
41
+ public function next()
42
+ {
43
+ next($this->values);
44
+ }
45
+
46
+ public function current()
47
+ {
48
+ return current($this->values);
49
+ }
50
+
51
+ public function key()
52
+ {
53
+ return key($this->values);
54
+ }
55
+ }
vendor/symfony/finder/Tests/Iterator/IteratorTestCase.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ use PHPUnit\Framework\TestCase;
15
+
16
+ abstract class IteratorTestCase extends TestCase
17
+ {
18
+ protected function assertIterator($expected, \Traversable $iterator)
19
+ {
20
+ // set iterator_to_array $use_key to false to avoid values merge
21
+ // this made FinderTest::testAppendWithAnArray() fail with GnuFinderAdapter
22
+ $values = array_map(function (\SplFileInfo $fileinfo) { return str_replace('/', DIRECTORY_SEPARATOR, $fileinfo->getPathname()); }, iterator_to_array($iterator, false));
23
+
24
+ $expected = array_map(function ($path) { return str_replace('/', DIRECTORY_SEPARATOR, $path); }, $expected);
25
+
26
+ sort($values);
27
+ sort($expected);
28
+
29
+ $this->assertEquals($expected, array_values($values));
30
+ }
31
+
32
+ protected function assertOrderedIterator($expected, \Traversable $iterator)
33
+ {
34
+ $values = array_map(function (\SplFileInfo $fileinfo) { return $fileinfo->getPathname(); }, iterator_to_array($iterator));
35
+
36
+ $this->assertEquals($expected, array_values($values));
37
+ }
38
+
39
+ /**
40
+ * Same as assertOrderedIterator, but checks the order of groups of
41
+ * array elements.
42
+ *
43
+ * @param array $expected - an array of arrays. For any two subarrays
44
+ * $a and $b such that $a goes before $b in $expected, the method
45
+ * asserts that any element of $a goes before any element of $b
46
+ * in the sequence generated by $iterator
47
+ * @param \Traversable $iterator
48
+ */
49
+ protected function assertOrderedIteratorForGroups($expected, \Traversable $iterator)
50
+ {
51
+ $values = array_values(array_map(function (\SplFileInfo $fileinfo) { return $fileinfo->getPathname(); }, iterator_to_array($iterator)));
52
+
53
+ foreach ($expected as $subarray) {
54
+ $temp = array();
55
+ while (count($values) && count($temp) < count($subarray)) {
56
+ $temp[] = array_shift($values);
57
+ }
58
+ sort($temp);
59
+ sort($subarray);
60
+ $this->assertEquals($subarray, $temp);
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Same as IteratorTestCase::assertIterator with foreach usage.
66
+ *
67
+ * @param array $expected
68
+ * @param \Traversable $iterator
69
+ */
70
+ protected function assertIteratorInForeach($expected, \Traversable $iterator)
71
+ {
72
+ $values = array();
73
+ foreach ($iterator as $file) {
74
+ $this->assertInstanceOf('Symfony\\Component\\Finder\\SplFileInfo', $file);
75
+ $values[] = $file->getPathname();
76
+ }
77
+
78
+ sort($values);
79
+ sort($expected);
80
+
81
+ $this->assertEquals($expected, array_values($values));
82
+ }
83
+
84
+ /**
85
+ * Same as IteratorTestCase::assertOrderedIterator with foreach usage.
86
+ *
87
+ * @param array $expected
88
+ * @param \Traversable $iterator
89
+ */
90
+ protected function assertOrderedIteratorInForeach($expected, \Traversable $iterator)
91
+ {
92
+ $values = array();
93
+ foreach ($iterator as $file) {
94
+ $this->assertInstanceOf('Symfony\\Component\\Finder\\SplFileInfo', $file);
95
+ $values[] = $file->getPathname();
96
+ }
97
+
98
+ $this->assertEquals($expected, array_values($values));
99
+ }
100
+ }
vendor/symfony/finder/Tests/Iterator/MockFileListIterator.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ class MockFileListIterator extends \ArrayIterator
15
+ {
16
+ public function __construct(array $filesArray = array())
17
+ {
18
+ $files = array_map(function ($file) { return new MockSplFileInfo($file); }, $filesArray);
19
+ parent::__construct($files);
20
+ }
21
+ }
vendor/symfony/finder/Tests/Iterator/MockSplFileInfo.php ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ class MockSplFileInfo extends \SplFileInfo
15
+ {
16
+ const TYPE_DIRECTORY = 1;
17
+ const TYPE_FILE = 2;
18
+ const TYPE_UNKNOWN = 3;
19
+
20
+ private $contents = null;
21
+ private $mode = null;
22
+ private $type = null;
23
+ private $relativePath = null;
24
+ private $relativePathname = null;
25
+
26
+ public function __construct($param)
27
+ {
28
+ if (is_string($param)) {
29
+ parent::__construct($param);
30
+ } elseif (is_array($param)) {
31
+ $defaults = array(
32
+ 'name' => 'file.txt',
33
+ 'contents' => null,
34
+ 'mode' => null,
35
+ 'type' => null,
36
+ 'relativePath' => null,
37
+ 'relativePathname' => null,
38
+ );
39
+ $defaults = array_merge($defaults, $param);
40
+ parent::__construct($defaults['name']);
41
+ $this->setContents($defaults['contents']);
42
+ $this->setMode($defaults['mode']);
43
+ $this->setType($defaults['type']);
44
+ $this->setRelativePath($defaults['relativePath']);
45
+ $this->setRelativePathname($defaults['relativePathname']);
46
+ } else {
47
+ throw new \RuntimeException(sprintf('Incorrect parameter "%s"', $param));
48
+ }
49
+ }
50
+
51
+ public function isFile()
52
+ {
53
+ if (null === $this->type) {
54
+ return false !== strpos($this->getFilename(), 'file');
55
+ }
56
+
57
+ return self::TYPE_FILE === $this->type;
58
+ }
59
+
60
+ public function isDir()
61
+ {
62
+ if (null === $this->type) {
63
+ return false !== strpos($this->getFilename(), 'directory');
64
+ }
65
+
66
+ return self::TYPE_DIRECTORY === $this->type;
67
+ }
68
+
69
+ public function isReadable()
70
+ {
71
+ if (null === $this->mode) {
72
+ return preg_match('/r\+/', $this->getFilename());
73
+ }
74
+
75
+ return preg_match('/r\+/', $this->mode);
76
+ }
77
+
78
+ public function getContents()
79
+ {
80
+ return $this->contents;
81
+ }
82
+
83
+ public function setContents($contents)
84
+ {
85
+ $this->contents = $contents;
86
+ }
87
+
88
+ public function setMode($mode)
89
+ {
90
+ $this->mode = $mode;
91
+ }
92
+
93
+ public function setType($type)
94
+ {
95
+ if (is_string($type)) {
96
+ switch ($type) {
97
+ case 'directory':
98
+ case 'd':
99
+ $this->type = self::TYPE_DIRECTORY;
100
+ break;
101
+ case 'file':
102
+ case 'f':
103
+ $this->type = self::TYPE_FILE;
104
+ break;
105
+ default:
106
+ $this->type = self::TYPE_UNKNOWN;
107
+ }
108
+ } else {
109
+ $this->type = $type;
110
+ }
111
+ }
112
+
113
+ public function setRelativePath($relativePath)
114
+ {
115
+ $this->relativePath = $relativePath;
116
+ }
117
+
118
+ public function setRelativePathname($relativePathname)
119
+ {
120
+ $this->relativePathname = $relativePathname;
121
+ }
122
+
123
+ public function getRelativePath()
124
+ {
125
+ return $this->relativePath;
126
+ }
127
+
128
+ public function getRelativePathname()
129
+ {
130
+ return $this->relativePathname;
131
+ }
132
+ }
vendor/symfony/finder/Tests/Iterator/MultiplePcreFilterIteratorTest.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ use PHPUnit\Framework\TestCase;
15
+ use Symfony\Component\Finder\Iterator\MultiplePcreFilterIterator;
16
+
17
+ class MultiplePcreFilterIteratorTest extends TestCase
18
+ {
19
+ /**
20
+ * @dataProvider getIsRegexFixtures
21
+ */
22
+ public function testIsRegex($string, $isRegex, $message)
23
+ {
24
+ $testIterator = new TestMultiplePcreFilterIterator();
25
+ $this->assertEquals($isRegex, $testIterator->isRegex($string), $message);
26
+ }
27
+
28
+ public function getIsRegexFixtures()
29
+ {
30
+ return array(
31
+ array('foo', false, 'string'),
32
+ array(' foo ', false, '" " is not a valid delimiter'),
33
+ array('\\foo\\', false, '"\\" is not a valid delimiter'),
34
+ array('afooa', false, '"a" is not a valid delimiter'),
35
+ array('//', false, 'the pattern should contain at least 1 character'),
36
+ array('/a/', true, 'valid regex'),
37
+ array('/foo/', true, 'valid regex'),
38
+ array('/foo/i', true, 'valid regex with a single modifier'),
39
+ array('/foo/imsxu', true, 'valid regex with multiple modifiers'),
40
+ array('#foo#', true, '"#" is a valid delimiter'),
41
+ array('{foo}', true, '"{,}" is a valid delimiter pair'),
42
+ array('[foo]', true, '"[,]" is a valid delimiter pair'),
43
+ array('(foo)', true, '"(,)" is a valid delimiter pair'),
44
+ array('<foo>', true, '"<,>" is a valid delimiter pair'),
45
+ array('*foo.*', false, '"*" is not considered as a valid delimiter'),
46
+ array('?foo.?', false, '"?" is not considered as a valid delimiter'),
47
+ );
48
+ }
49
+ }
50
+
51
+ class TestMultiplePcreFilterIterator extends MultiplePcreFilterIterator
52
+ {
53
+ public function __construct()
54
+ {
55
+ }
56
+
57
+ public function accept()
58
+ {
59
+ throw new \BadFunctionCallException('Not implemented');
60
+ }
61
+
62
+ public function isRegex($str)
63
+ {
64
+ return parent::isRegex($str);
65
+ }
66
+
67
+ public function toRegex($str)
68
+ {
69
+ throw new \BadFunctionCallException('Not implemented');
70
+ }
71
+ }
vendor/symfony/finder/Tests/Iterator/PathFilterIteratorTest.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ use Symfony\Component\Finder\Iterator\PathFilterIterator;
15
+
16
+ class PathFilterIteratorTest extends IteratorTestCase
17
+ {
18
+ /**
19
+ * @dataProvider getTestFilterData
20
+ */
21
+ public function testFilter(\Iterator $inner, array $matchPatterns, array $noMatchPatterns, array $resultArray)
22
+ {
23
+ $iterator = new PathFilterIterator($inner, $matchPatterns, $noMatchPatterns);
24
+ $this->assertIterator($resultArray, $iterator);
25
+ }
26
+
27
+ public function getTestFilterData()
28
+ {
29
+ $inner = new MockFileListIterator();
30
+
31
+ //PATH: A/B/C/abc.dat
32
+ $inner[] = new MockSplFileInfo(array(
33
+ 'name' => 'abc.dat',
34
+ 'relativePathname' => 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat',
35
+ ));
36
+
37
+ //PATH: A/B/ab.dat
38
+ $inner[] = new MockSplFileInfo(array(
39
+ 'name' => 'ab.dat',
40
+ 'relativePathname' => 'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat',
41
+ ));
42
+
43
+ //PATH: A/a.dat
44
+ $inner[] = new MockSplFileInfo(array(
45
+ 'name' => 'a.dat',
46
+ 'relativePathname' => 'A'.DIRECTORY_SEPARATOR.'a.dat',
47
+ ));
48
+
49
+ //PATH: copy/A/B/C/abc.dat.copy
50
+ $inner[] = new MockSplFileInfo(array(
51
+ 'name' => 'abc.dat.copy',
52
+ 'relativePathname' => 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'C'.DIRECTORY_SEPARATOR.'abc.dat',
53
+ ));
54
+
55
+ //PATH: copy/A/B/ab.dat.copy
56
+ $inner[] = new MockSplFileInfo(array(
57
+ 'name' => 'ab.dat.copy',
58
+ 'relativePathname' => 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'B'.DIRECTORY_SEPARATOR.'ab.dat',
59
+ ));
60
+
61
+ //PATH: copy/A/a.dat.copy
62
+ $inner[] = new MockSplFileInfo(array(
63
+ 'name' => 'a.dat.copy',
64
+ 'relativePathname' => 'copy'.DIRECTORY_SEPARATOR.'A'.DIRECTORY_SEPARATOR.'a.dat',
65
+ ));
66
+
67
+ return array(
68
+ array($inner, array('/^A/'), array(), array('abc.dat', 'ab.dat', 'a.dat')),
69
+ array($inner, array('/^A\/B/'), array(), array('abc.dat', 'ab.dat')),
70
+ array($inner, array('/^A\/B\/C/'), array(), array('abc.dat')),
71
+ array($inner, array('/A\/B\/C/'), array(), array('abc.dat', 'abc.dat.copy')),
72
+
73
+ array($inner, array('A'), array(), array('abc.dat', 'ab.dat', 'a.dat', 'abc.dat.copy', 'ab.dat.copy', 'a.dat.copy')),
74
+ array($inner, array('A/B'), array(), array('abc.dat', 'ab.dat', 'abc.dat.copy', 'ab.dat.copy')),
75
+ array($inner, array('A/B/C'), array(), array('abc.dat', 'abc.dat.copy')),
76
+
77
+ array($inner, array('copy/A'), array(), array('abc.dat.copy', 'ab.dat.copy', 'a.dat.copy')),
78
+ array($inner, array('copy/A/B'), array(), array('abc.dat.copy', 'ab.dat.copy')),
79
+ array($inner, array('copy/A/B/C'), array(), array('abc.dat.copy')),
80
+ );
81
+ }
82
+ }
vendor/symfony/finder/Tests/Iterator/RealIteratorTestCase.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ abstract class RealIteratorTestCase extends IteratorTestCase
15
+ {
16
+ protected static $tmpDir;
17
+ protected static $files;
18
+
19
+ public static function setUpBeforeClass()
20
+ {
21
+ self::$tmpDir = realpath(sys_get_temp_dir()).DIRECTORY_SEPARATOR.'symfony_finder';
22
+
23
+ self::$files = array(
24
+ '.git/',
25
+ '.foo/',
26
+ '.foo/.bar',
27
+ '.foo/bar',
28
+ '.bar',
29
+ 'test.py',
30
+ 'foo/',
31
+ 'foo/bar.tmp',
32
+ 'test.php',
33
+ 'toto/',
34
+ 'toto/.git/',
35
+ 'foo bar',
36
+ );
37
+
38
+ self::$files = self::toAbsolute(self::$files);
39
+
40
+ if (is_dir(self::$tmpDir)) {
41
+ self::tearDownAfterClass();
42
+ } else {
43
+ mkdir(self::$tmpDir);
44
+ }
45
+
46
+ foreach (self::$files as $file) {
47
+ if (DIRECTORY_SEPARATOR === $file[strlen($file) - 1]) {
48
+ mkdir($file);
49
+ } else {
50
+ touch($file);
51
+ }
52
+ }
53
+
54
+ file_put_contents(self::toAbsolute('test.php'), str_repeat(' ', 800));
55
+ file_put_contents(self::toAbsolute('test.py'), str_repeat(' ', 2000));
56
+
57
+ touch(self::toAbsolute('foo/bar.tmp'), strtotime('2005-10-15'));
58
+ touch(self::toAbsolute('test.php'), strtotime('2005-10-15'));
59
+ }
60
+
61
+ public static function tearDownAfterClass()
62
+ {
63
+ foreach (array_reverse(self::$files) as $file) {
64
+ if (DIRECTORY_SEPARATOR === $file[strlen($file) - 1]) {
65
+ @rmdir($file);
66
+ } else {
67
+ @unlink($file);
68
+ }
69
+ }
70
+ }
71
+
72
+ protected static function toAbsolute($files = null)
73
+ {
74
+ /*
75
+ * Without the call to setUpBeforeClass() property can be null.
76
+ */
77
+ if (!self::$tmpDir) {
78
+ self::$tmpDir = realpath(sys_get_temp_dir()).DIRECTORY_SEPARATOR.'symfony_finder';
79
+ }
80
+
81
+ if (is_array($files)) {
82
+ $f = array();
83
+ foreach ($files as $file) {
84
+ if (is_array($file)) {
85
+ $f[] = self::toAbsolute($file);
86
+ } else {
87
+ $f[] = self::$tmpDir.DIRECTORY_SEPARATOR.str_replace('/', DIRECTORY_SEPARATOR, $file);
88
+ }
89
+ }
90
+
91
+ return $f;
92
+ }
93
+
94
+ if (is_string($files)) {
95
+ return self::$tmpDir.DIRECTORY_SEPARATOR.str_replace('/', DIRECTORY_SEPARATOR, $files);
96
+ }
97
+
98
+ return self::$tmpDir;
99
+ }
100
+
101
+ protected static function toAbsoluteFixtures($files)
102
+ {
103
+ $f = array();
104
+ foreach ($files as $file) {
105
+ $f[] = realpath(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.$file);
106
+ }
107
+
108
+ return $f;
109
+ }
110
+ }
vendor/symfony/finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator;
15
+
16
+ class RecursiveDirectoryIteratorTest extends IteratorTestCase
17
+ {
18
+ /**
19
+ * @group network
20
+ */
21
+ public function testRewindOnFtp()
22
+ {
23
+ try {
24
+ $i = new RecursiveDirectoryIterator('ftp://speedtest.tele2.net/', \RecursiveDirectoryIterator::SKIP_DOTS);
25
+ } catch (\UnexpectedValueException $e) {
26
+ $this->markTestSkipped('Unsupported stream "ftp".');
27
+ }
28
+
29
+ $i->rewind();
30
+
31
+ $this->assertTrue(true);
32
+ }
33
+
34
+ /**
35
+ * @group network
36
+ */
37
+ public function testSeekOnFtp()
38
+ {
39
+ try {
40
+ $i = new RecursiveDirectoryIterator('ftp://speedtest.tele2.net/', \RecursiveDirectoryIterator::SKIP_DOTS);
41
+ } catch (\UnexpectedValueException $e) {
42
+ $this->markTestSkipped('Unsupported stream "ftp".');
43
+ }
44
+
45
+ $contains = array(
46
+ 'ftp://speedtest.tele2.net'.DIRECTORY_SEPARATOR.'1000GB.zip',
47
+ 'ftp://speedtest.tele2.net'.DIRECTORY_SEPARATOR.'100GB.zip',
48
+ );
49
+ $actual = array();
50
+
51
+ $i->seek(0);
52
+ $actual[] = $i->getPathname();
53
+
54
+ $i->seek(1);
55
+ $actual[] = $i->getPathname();
56
+
57
+ $this->assertEquals($contains, $actual);
58
+ }
59
+ }
vendor/symfony/finder/Tests/Iterator/SizeRangeFilterIteratorTest.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator;
15
+ use Symfony\Component\Finder\Comparator\NumberComparator;
16
+
17
+ class SizeRangeFilterIteratorTest extends RealIteratorTestCase
18
+ {
19
+ /**
20
+ * @dataProvider getAcceptData
21
+ */
22
+ public function testAccept($size, $expected)
23
+ {
24
+ $inner = new InnerSizeIterator(self::$files);
25
+
26
+ $iterator = new SizeRangeFilterIterator($inner, $size);
27
+
28
+ $this->assertIterator($expected, $iterator);
29
+ }
30
+
31
+ public function getAcceptData()
32
+ {
33
+ $lessThan1KGreaterThan05K = array(
34
+ '.foo',
35
+ '.git',
36
+ 'foo',
37
+ 'test.php',
38
+ 'toto',
39
+ 'toto/.git',
40
+ );
41
+
42
+ return array(
43
+ array(array(new NumberComparator('< 1K'), new NumberComparator('> 0.5K')), $this->toAbsolute($lessThan1KGreaterThan05K)),
44
+ );
45
+ }
46
+ }
47
+
48
+ class InnerSizeIterator extends \ArrayIterator
49
+ {
50
+ public function current()
51
+ {
52
+ return new \SplFileInfo(parent::current());
53
+ }
54
+
55
+ public function getFilename()
56
+ {
57
+ return parent::current();
58
+ }
59
+
60
+ public function isFile()
61
+ {
62
+ return $this->current()->isFile();
63
+ }
64
+
65
+ public function getSize()
66
+ {
67
+ return $this->current()->getSize();
68
+ }
69
+ }
vendor/symfony/finder/Tests/Iterator/SortableIteratorTest.php ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of the Symfony package.
5
+ *
6
+ * (c) Fabien Potencier <fabien@symfony.com>
7
+ *
8
+ * For the full copyright and license information, please view the LICENSE
9
+ * file that was distributed with this source code.
10
+ */
11
+
12
+ namespace Symfony\Component\Finder\Tests\Iterator;
13
+
14
+ use Symfony\Component\Finder\Iterator\SortableIterator;
15
+
16
+ class SortableIteratorTest extends RealIteratorTestCase
17
+ {
18
+ public function testConstructor()
19
+ {
20
+ try {
21
+ new SortableIterator(new Iterator(array()), 'foobar');
22
+ $this->fail('__construct() throws an \InvalidArgumentException exception if the mode is not valid');
23
+ } catch (\Exception $e) {
24
+ $this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException exception if the mode is not valid');
25
+ }
26
+ }
27
+
28
+ /**
29
+ * @dataProvider getAcceptData
30
+ */
31
+ public function testAccept($mode, $expected)
32
+ {
33
+ if (!is_callable($mode)) {
34
+ switch ($mode) {
35
+ case SortableIterator::SORT_BY_ACCESSED_TIME:
36
+ if ('\\' === DIRECTORY_SEPARATOR) {
37
+ touch(self::toAbsolute('.git'));
38
+ } else {
39
+ file_get_contents(self::toAbsolute('.git'));
40
+ }
41
+ sleep(1);
42
+ file_get_contents(self::toAbsolute('.bar'));
43
+ break;
44
+ case SortableIterator::SORT_BY_CHANGED_TIME:
45
+ file_put_contents(self::toAbsolute('test.php'), 'foo');
46
+ sleep(1);
47
+ file_put_contents(self::toAbsolute('test.py'), 'foo');
48
+ break;
49
+ case SortableIterator::SORT_BY_MODIFIED_TIME:
50
+ file_put_contents(self::toAbsolute('test.php'), 'foo');
51
+ sleep(1);
52
+ file_put_contents(self::toAbsolute('test.py'), 'foo');
53
+ break;
54
+ }
55
+ }
56
+
57
+ $inner = new Iterator(self::$files);
58
+
59
+ $iterator = new SortableIterator($inner, $mode);
60
+
61
+ if (SortableIterator::SORT_BY_ACCESSED_TIME === $mode
62
+ || SortableIterator::SORT_BY_CHANGED_TIME === $mode
63
+ || SortableIterator::SORT_BY_MODIFIED_TIME === $mode
64
+ ) {
65
+ if ('\\' === DIRECTORY_SEPARATOR && SortableIterator::SORT_BY_MODIFIED_TIME !== $mode) {
66
+ $this->markTestSkipped('Sorting by atime or ctime is not supported on Windows');
67
+ }
68
+ $this->assertOrderedIteratorForGroups($expected, $iterator);
69
+ } else {
70
+ $this->assertOrderedIterator($expected, $iterator);
71
+ }
72
+ }
73
+
74
+ public function getAcceptData()
75
+ {
76
+ $sortByName = array(
77
+ '.bar',
78
+ '.foo',
79
+ '.foo/.bar',
80
+ '.foo/bar',
81
+ '.git',
82
+ 'foo',
83
+ 'foo bar',
84
+ 'foo/bar.tmp',
85
+ 'test.php',
86
+ 'test.py',
87
+ 'toto',
88
+ 'toto/.git',
89
+ );
90
+
91
+ $sortByType = array(
92
+ '.foo',
93
+ '.git',
94
+ 'foo',
95
+ 'toto',
96
+ 'toto/.git',
97
+ '.bar',
98
+ '.foo/.bar',
99
+ '.foo/bar',
100
+ 'foo bar',
101
+ 'foo/bar.tmp',
102
+ 'test.php',
103
+ 'test.py',
104
+ );
105
+
106
+ $customComparison = array(
107
+ '.bar',
108
+ '.foo',
109
+ '.foo/.bar',
110
+ '.foo/bar',
111
+ '.git',
112
+ 'foo',
113
+ 'foo bar',
114
+ 'foo/bar.tmp',
115
+ 'test.php',
116
+ 'test.py',
117
+ 'toto',
118
+ 'toto/.git',
119
+ );
120
+
121
+ $sortByAccessedTime = array(
122
+ // For these two files the access time was set to 2005-10-15
123
+ array('foo/bar.tmp', 'test.php'),
124
+ // These files were created more or less at the same time
125
+ array(
126
+ '.git',
127
+ '.foo',
128
+ '.foo/.bar',
129
+ '.foo/bar',
130
+ 'test.py',
131
+ 'foo',
132
+ 'toto',
133
+ 'toto/.git',
134
+ 'foo bar',
135
+ ),
136
+ // This file was accessed after sleeping for 1 sec
137
+ array('.bar'),
138
+ );
139
+
140
+ $sortByChangedTime = array(
141
+ array(
142
+ '.git',
143
+ '.foo',
144
+ '.foo/.bar',
145
+ '.foo/bar',
146
+ '.bar',
147
+ 'foo',
148
+ 'foo/bar.tmp',
149
+ 'toto',
150
+ 'toto/.git',
151
+ 'foo bar',
152
+ ),
153
+ array('test.php'),
154
+ array('test.py'),
155
+ );
156
+
157
+ $sortByModifiedTime = array(
158
+ array(
159
+ '.git',
160
+ '.foo',
161
+ '.foo/.bar',
162
+ '.foo/bar',
163
+ '.bar',
164
+ 'foo',
165
+ 'foo/bar.tmp',
166
+ 'toto',
167
+ 'toto/.git',
168
+ 'foo bar',
169
+ ),
170
+ array('test.php'),
171
+ array('test.py'),
172
+ );
173
+
174
+ return array(
175
+ array(SortableIterator::SORT_BY_NAME, $this->toAbsolute($sortByName)),
176
+ array(SortableIterator::SORT_BY_TYPE, $this->toAbsolute($sortByType)),
177
+ array(SortableIterator::SORT_BY_ACCESSED_TIME, $this->toAbsolute($sortByAccessedTime)),
178
+ array(SortableIterator::SORT_BY_CHANGED_TIME, $this->toAbsolute($sortByChangedTime)),
179
+ array(SortableIterator::SORT_BY_MODIFIED_TIME, $this->toAbsolute($sortByModifiedTime)),
180
+ array(function (\SplFileInfo $a, \SplFileInfo $b) { return strcmp($a->getRealPath(), $b->getRealPath()); }, $this->toAbsolute($customComparison)),
181
+ );
182
+ }
183
+ }
vendor/symfony/finder/composer.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "symfony/finder",
3
+ "type": "library",
4
+ "description": "Symfony Finder Component",
5
+ "keywords": [],
6
+ "homepage": "https://symfony.com",
7
+ "license": "MIT",
8
+ "authors": [
9
+ {
10
+ "name": "Fabien Potencier",
11
+ "email": "fabien@symfony.com"
12
+ },
13
+ {
14
+ "name": "Symfony Community",
15
+ "homepage": "https://symfony.com/contributors"
16
+ }
17
+ ],
18
+ "require": {
19
+ "php": "^5.5.9|>=7.0.8"
20
+ },
21
+ "autoload": {
22
+ "psr-4": { "Symfony\\Component\\Finder\\": "" },
23
+ "exclude-from-classmap": [
24
+ "/Tests/"
25
+ ]
26
+ },
27
+ "minimum-stability": "dev",
28
+ "extra": {
29
+ "branch-alias": {
30
+ "dev-master": "3.4-dev"
31
+ }
32
+ }
33
+ }
vendor/symfony/finder/phpunit.xml.dist ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
5
+ backupGlobals="false"
6
+ colors="true"
7
+ bootstrap="vendor/autoload.php"
8
+ failOnRisky="true"
9
+ failOnWarning="true"
10
+ >
11
+ <php>
12
+ <ini name="error_reporting" value="-1" />
13
+ </php>
14
+
15
+ <testsuites>
16
+ <testsuite name="Symfony Finder Component Test Suite">
17
+ <directory>./Tests/</directory>
18
+ </testsuite>
19
+ </testsuites>
20
+
21
+ <filter>
22
+ <whitelist>
23
+ <directory>./</directory>
24
+ <exclude>
25
+ <directory>./Tests</directory>
26
+ <directory>./vendor</directory>
27
+ </exclude>
28
+ </whitelist>
29
+ </filter>
30
+ </phpunit>
views/admin/consent.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <hr>
2
+
3
+ <h3><?= __('Default consent types', 'gdpr-admin'); ?></h3>
4
+ <p><?= __('These are the consent types that have been automatically registered by the framework or a plugin.', 'gdpr-admin'); ?></p>
5
+ <?php if (count($defaultConsentTypes)): ?>
6
+ <table class="gdpr-consent">
7
+ <th><?= __('Slug', 'gdpr-admin'); ?></th>
8
+ <th><?= __('Title', 'gdpr-admin'); ?></th>
9
+ <th><?= __('Description', 'gdpr-admin'); ?></th>
10
+ <th><?= __('Visibility', 'gdpr-admin'); ?></th>
11
+ <?php foreach ($defaultConsentTypes as $consentType): ?>
12
+ <tr>
13
+ <td class="gdpr-consent-table-input"><?= $consentType['slug']; ?></td>
14
+ <td class="gdpr-consent-table-input"><?= $consentType['title']; ?></td>
15
+ <td class="gdpr-consent-table-desc"><?= $consentType['description']; ?></td>
16
+ <td>
17
+ <?php if ($consentType['visible']): ?>
18
+ <?= __('Visible', 'gdpr-admin'); ?>
19
+ <?php else: ?>
20
+ <?= __('Hidden', 'gdpr-admin'); ?>
21
+ <?php endif; ?>
22
+ </td>
23
+ </tr>
24
+ <?php endforeach; ?>
25
+ </table>
26
+ <?php endif; ?>
27
+ <br>
28
+ <hr>
29
+ <h3><?= __('Custom consent types', 'gdpr-admin'); ?></h3>
30
+ <p><?= __('Here you can add custom consent types to track. They will not be used anywhere by default - you will need to build an integration for each of them.', 'gdpr-admin'); ?></p>
31
+ <div class="js-gdpr-repeater" data-name="gdpr_consent_types">
32
+ <table class="gdpr-consent-admin" data-repeater-list="gdpr_consent_types">
33
+ <thead>
34
+ <th>
35
+ <?= __('Machine-readable slug', 'gdpr-admin'); ?>*
36
+ </th>
37
+ <th>
38
+ <?= __('Title', 'gdpr-admin'); ?>*
39
+ </th>
40
+ <th>
41
+ <?= __('Description', 'gdpr-admin'); ?>
42
+ </th>
43
+ <th>
44
+ <?= __('Visible?', 'gdpr-admin'); ?>
45
+ </th>
46
+ </thead>
47
+ <tr data-repeater-item>
48
+ <td class="gdpr-consent-table-input">
49
+ <input
50
+ type="text"
51
+ name="slug"
52
+ placeholder="<?= __('Slug', 'gdpr-admin'); ?>"
53
+ pattern="^[A-Za-z0-9_-]+$"
54
+ oninvalid="setCustomValidity('Please fill in this field using alphanumeric characters, dashes and underscores.')"
55
+ oninput="setCustomValidity('')"
56
+ required
57
+ />
58
+ </td>
59
+ <td class="gdpr-consent-table-input">
60
+ <input type="text" name="title" placeholder="<?= __('Title', 'gdpr-admin'); ?>" required />
61
+ </td>
62
+ <td class="gdpr-consent-table-desc">
63
+ <textarea type="text" name="description" placeholder="<?= __('Description', 'gdpr-admin'); ?>"></textarea>
64
+ </td>
65
+ <td>
66
+ <label>
67
+ <input type="checkbox" name="visible" value="1"/>
68
+ <?= __('Visible?', 'gdpr-admin'); ?>
69
+ </label>
70
+ </td>
71
+ <td>
72
+ <input data-repeater-delete class="button button-primary" type="button" value="<?= __('Remove', 'gdpr-admin'); ?>"/>
73
+ </td>
74
+ </tr>
75
+
76
+ </table>
77
+ <div class="gdpr-consent-add-button">
78
+ <input data-repeater-create class="button button-primary" type="button" value="Add consent type"/>
79
+ </div>
80
+ <input type="hidden" name="gdpr_nonce" value="<?= $nonce; ?>" />
81
+ <input type="hidden" name="gdpr_action" value="update_consent_data" />
82
+ </div>
83
+
84
+ <?php if (count($customConsentTypes)): ?>
85
+ <script>
86
+ window.repeaterData = [];
87
+ window.repeaterData['gdpr_consent_types'] = <?= json_encode($customConsentTypes); ?>;
88
+ </script>
89
+ <?php endif; ?>
90
+
91
+ <br>
92
+ <hr>
93
+ <h3><?= __('Additional info', 'gdpr-admin'); ?></h3>
94
+ <p>
95
+ <?= __('This text will be displayed to your data subjects on the Privacy Tools page.', 'gdpr-admin'); ?>
96
+ </p>
97
+ <?php wp_editor(
98
+ wp_kses_post($consentInfo),
99
+ 'gdpr_consent_info',
100
+ [
101
+ 'textarea_rows' => 4,
102
+ ]
103
+ );
104
+ ?>
105
+
views/admin/data-subjects/search-form.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>
2
+ <?= __('On this page, you can find which data subjects personal data you are storing and download, export or delete it.', 'gdpr-admin'); ?>
3
+ </p>
4
+
5
+ <hr>
6
+
7
+ <?= $results; ?>
8
+
9
+ <label>
10
+ <h3><?= __('Find data subject by email', 'gdpr-admin'); ?></h3>
11
+ <input type="email" name="gdpr_email" placeholder="<?= __('Email address', 'gdpr-admin'); ?>" />
12
+ </label>
13
+
14
+ <input type="hidden" name="gdpr_nonce" value="<?= $nonce; ?>" />
15
+ <input type="hidden" name="gdpr_action" value="search" />
16
+ <input class="button button-primary" type="submit" value="<?= __('Search', 'gdpr-admin'); ?>" />
17
+
18
+ <br><br>
views/admin/data-subjects/search-results.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div>
2
+ <h3>Results for: <?= esc_html($email); ?></h3>
3
+ <?php if ($hasData): ?>
4
+
5
+ <?php if (isset($links['profile'])): ?>
6
+ <p>
7
+ <strong><?= __('Username', 'gdpr-admin'); ?>:</strong>
8
+ <a href="<?= $links['profile']; ?>"><?= esc_html($userName); ?></a>
9
+ </p>
10
+ <?php else: ?>
11
+ <p>
12
+ <em><?= esc_html($email); ?> <?= __('is not a registered user.', 'gdpr-admin'); ?></em>
13
+ </p>
14
+ <?php endif; ?>
15
+
16
+ <a class="button button-primary" href="<?= esc_url($links['view']); ?>"><?= __('Download data (html)', 'gdpr-admin'); ?></a>
17
+ <a class="button button-primary" href="<?= esc_url($links['export']); ?>"><?= __('Export data (json)', 'gdpr-admin'); ?></a>
18
+
19
+ <?php if ($adminCap): ?>
20
+ <p>
21
+ <strong><?= __('This user has admin capabilities. Deleting data via this interface is disabled.', 'gdpr-admin'); ?></strong>
22
+ </p>
23
+ <?php else: ?>
24
+ <a class="button button-primary" href="<?= esc_url($links['anonymize']); ?>"><?= __('Anonymize data', 'gdpr-admin'); ?></a>
25
+ <a class="button button-primary" href="<?= esc_url($links['delete']); ?>"><?= __('Delete data', 'gdpr-admin'); ?></a>
26
+ <?php endif; ?>
27
+
28
+ <?php else: ?>
29
+ <p><?= __('No data found!', 'gdpr-admin'); ?></p>
30
+ <?php endif; ?>
31
+ </div>
32
+ <br>
33
+ <hr>
views/admin/general/delete-action-email.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <input
2
+ type="email"
3
+ id="gdpr_delete_action_email"
4
+ name="gdpr_delete_action_email"
5
+ placeholder="<?= __('Email address', 'gdpr'); ?>"
6
+ value="<?= esc_attr($deleteActionEmail); ?>"
7
+ />
views/admin/general/delete-action-reassign.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <select id="gdpr_delete_action_reassign" name="gdpr_delete_action_reassign" class="gdpr-select js-gdpr-conditional">
2
+ <option value="delete" <?= selected($reassign, 'delete'); ?>>
3
+ <?= __('Delete content', 'gdpr-admin'); ?>
4
+ </option>
5
+ <option value="reassign" <?= selected($reassign, 'reassign'); ?> data-show=".js-gdpr-delete-action-reassign-user">
6
+ <?= __('Reassign content to a user', 'gdpr-admin'); ?>
7
+ </option>
8
+ </select>
9
+ <p class="description">
10
+ <?= __('If the user has submitted any content on your site, should it be deleted or reassigned to another user?', 'gdpr-admin'); ?>
11
+ </p>
views/admin/general/delete-action.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <select class="gdpr-select js-gdpr-conditional" name="gdpr_delete_action">
2
+ <?= gdpr('view')->render('global/delete-action', compact('deleteAction')); ?>
3
+ </select>
views/admin/general/description-data-page.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <p class="description">
2
+ <?= __('Select the page where users can go to control their data. This page must contain the [gdpr_privacy_tools] shortcode.', 'gdpr-admin'); ?>
3
+ </p>
views/admin/general/description-delete-action.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <p class="description">
2
+ <?= __('What should happen if a data subject requests deleting their data.', 'gdpr-admin'); ?>
3
+ </p>
views/admin/general/description-export-action.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <p class="description">
2
+ <?= __('What should happen if a data subject requests viewing or exporting their data.', 'gdpr-admin'); ?>
3
+ </p>
views/admin/general/description-terms-page.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <p class="description">
2
+ <?= __('Optional. Select the page which contains your Terms & Conditions', 'gdpr-admin'); ?>
3
+ </p>
views/admin/general/enable.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <input
2
+ type="checkbox"
3
+ id="gdpr_enable"
4
+ name="gdpr_enable"
5
+ value="1"
6
+ <?= checked($enabled, true); ?>
7
+ />
8
+ <label for="gdpr_enable">
9
+ <?= __('Enable the view, export and forget functionality for users and visitors', 'gdpr-admin'); ?>
10
+ </label>
11
+ <p class="description">
12
+ <?= __('Enable the Privacy Tools page on front-end and dashboard. This allows visitors to request viewing and deleting their personal data and withdraw consents.', 'gdpr-admin'); ?>
13
+ </p>
views/admin/general/export-action-email.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <input
2
+ type="email"
3
+ id="gdpr_export_action_email"
4
+ name="gdpr_export_action_email"
5
+ placeholder="<?= __('Email address', 'gdpr'); ?>"
6
+ value="<?= esc_attr($exportActionEmail); ?>"
7
+ />
views/admin/general/export-action.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <select class="gdpr-select js-gdpr-conditional" name="gdpr_export_action">
2
+ <?= gdpr('view')->render('global/export-action', compact('exportAction')); ?>
3
+ </select>
views/admin/general/theme-compatibility.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <input
2
+ type="checkbox"
3
+ id="gdpr_enable_theme_compatibility"
4
+ name="gdpr_enable_theme_compatibility"
5
+ value="1"
6
+ <?= checked($enabled, true); ?>
7
+ />
8
+ <label for="gdpr_enable_theme_compatibility">
9
+ <?= __('Automatically add Privacy Policy and Privacy Tools links to your site footer.', 'gdpr-admin'); ?>
10
+ </label>
views/admin/modals/footer.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <div class="gdpr-modal-footer">
2
+ <a href="#" class="gdpr-close-modal">Close</a>
3
+ |
4
+ <a href="#">Get help</a>
5
+ </div>
6
+ </div>
views/admin/modals/header.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <div
2
+ id="<?= $data['id']; ?>"
3
+ class="hidden gdpr-modal"
4
+ data-gdpr-title="<?= isset($data['title']) ? $data['title'] : ''; ?>"
5
+ >
views/admin/modals/test.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <h2>Test modal</h2>
2
+ <p>This is a test modal. It can have any sort of content, for example a bullet list.</p>
3
+ <ul>
4
+ <li>Item 1</li>
5
+ <li>Another item</li>
6
+ <li>Last item</li>
7
+ </ul>
8
+ <p>It should be used for displaying super helpful instructions.</p>
views/admin/notice.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <div class="<?= $class; ?>">
2
+ <p><?= $message; ?></p>
3
+ </div>
views/admin/notices/disclaimer.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>
2
+ <strong>Disclaimer</strong><br>
3
+ Using The GDPR Framework does NOT guarantee compliance to GDPR.
4
+ By using this plugin, you agree to this disclaimer.
5
+ These templates and guides provided to you are drafted by legal professionals in order for you to understand your obligations better,
6
+ but they are NOT meant to constitute client-attorney relationship or personalized legal advice.
7
+ Codelight is not eligible for any claim or action based on any information or functionality provided by this plugin.
8
+ We expressly disclaim all liability in respect of usage of this plugin.
9
+ This plugin gives you general information and tools, but is NOT meant to serve as complete compliance package.
10
+ For compliance audit or further help <a href="<?= gdpr('helpers')->docs('wordpress-gdpr-consultation'); ?>">contact legal professionals</a>.
11
+ As each business and situation is unique, you might need to modify, add or delete information in these templates.
12
+ In addition to this, you will need audit all your processing activities for achieving compliance to GDPR.
13
+ Compliance to GDPR is risk based ongoing task.
14
+ We are here to get you started.
15
+ </p>
16
+ <a class="button button-primary" href="<?= esc_url($acceptUrl); ?>">I accept</a>
views/admin/notices/error.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <div class="notice notice-error">
2
+ <p><?= $message; ?></p>
3
+ </div>
views/admin/notices/footer-step.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ </form>
2
+ </div>
3
+ </div>
views/admin/notices/footer.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ </div>
2
+ </div>
views/admin/notices/header-step.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <div class="notice notice-gdpr">
2
+ <img class="gdpr-badge" src="<?= gdpr('config')->get('plugin.url'); ?>assets/gdpr-rhino.svg" />
3
+ <div class="gdpr-content">
4
+ <form method="POST">
5
+ <input type="hidden" name="gdpr-installer" value="next">
views/admin/notices/header.php ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <div class="notice notice-gdpr">
2
+ <img class="gdpr-badge" src="<?= gdpr('config')->get('plugin.url'); ?>assets/gdpr-rhino.svg" />
3
+ <div class="gdpr-content">
4
+ <h2><?= __('The GDPR Framework', 'gdpr-admin'); ?></h2>
views/admin/notices/help.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <p>
2
+ Need help? Take a look at our <a href="<?= gdpr('helpers')->docs('wordpress-site-owners-guide-to-gdpr/'); ?>" target="_blank">documentation</a>.
3
+ </p>
views/admin/notices/helper-autoinstall.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <p>
2
+ <?= __('A Privacy Policy page has been created, but it is empty. You can generate a policy template on this page.', 'gdpr-admin'); ?>
3
+ </p>
4
+ <p>
5
+ <?= __(
6
+ sprintf('Read more %shere%s', "<a href='{$helpUrl}'>", "</a>"),
7
+ 'gdpr-admin'
8
+ ); ?>
9
+ </p>
views/admin/notices/helper-policy.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <p>
2
+ <?= __('Heads up - your Privacy Policy still requires some attention. Find the places marked with [TODO] and replace them with real content!', 'gdpr-admin'); ?>
3
+ </p>
4
+ <p>
5
+ <?= __(
6
+ sprintf('Read more about editing your Privacy Policy %shere%s', "<a href='{$helpUrl}'>", "</a>"),
7
+ 'gdpr-admin'
8
+ ); ?>
9
+ </p>
views/admin/notices/helper-tools.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <p>
2
+ <?= __('The contents of this page should contain the [gdpr_tools] shortcode.', 'gdpr-admin'); ?>
3
+ </p>
4
+ <p>
5
+ <?= __(
6
+ sprintf('Read more %shere%s', "<a href='{$helpUrl}'>", "</a>"),
7
+ 'gdpr-admin'
8
+ ); ?>
9
+ </p>
views/admin/notices/installer-finished.php ADDED
@@ -0,0 +1 @@
 
1
+ <p>That's all! You have successfully set up</p>
views/admin/privacy-policy/company-location.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <select id="gdpr_company_location" name="gdpr_company_location" class="js-gdpr-select2 gdpr-select js-gdpr-conditional js-gdpr-country-selector">
2
+ <?= $countrySelectOptions; ?>
3
+ </select>
views/admin/privacy-policy/description-policy-page.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <p class="description">
2
+ <?= __('Select the page which will contain your Privacy Policy', 'gdpr-admin'); ?>
3
+ </p>
views/admin/privacy-policy/dpa.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <script>
2
+ window.gdprDpaData = <?= $dpaData; ?>;
3
+ </script>
views/admin/privacy-policy/generated.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h3><?= __('Privacy Policy', 'gdpr-admin'); ?></h3>
2
+ <p>
3
+ <?= __('Your Privacy Policy has been generated.', 'gdpr-admin'); ?>
4
+ <?php if ($policyUrl): ?>
5
+ <?= __(
6
+ sprintf(
7
+ 'You can copy and paste it to your %sPrivacy Policy page%s.',
8
+ "<a href='{$policyUrl}' target='_blank'>",
9
+ "</a>"
10
+ ),
11
+ 'gdpr-admin'
12
+ ); ?>
13
+ <?php endif; ?>
14
+ </p>
15
+
16
+ <?= $editor; ?>
17
+
18
+ <br>
19
+ <a href="<?= $backUrl; ?>" class="button button-secondary"><?= __('&laquo; Back', 'gdpr-admin'); ?></a>
20
+ <br><br>
views/admin/privacy-policy/has-dpo.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <label for="gdpr_has_dpo">
2
+ <input
3
+ type="checkbox"
4
+ name="gdpr_has_dpo"
5
+ id="gdpr_has_dpo"
6
+ class="js-gdpr-conditional"
7
+ data-show=".gdpr-dpo"
8
+ value="yes"
9
+ <?= checked($hasDPO, 'yes'); ?>
10
+ >
11
+ I have appointed a Data Protection Officer (DPO)
12
+ </label>
views/admin/privacy-policy/header.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <p>
2
+ <?= __('This page allows you to generate a Privacy Policy based on the information you entered below.', 'gdpr-admin'); ?>
3
+ </p>
views/admin/settings-page.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap gdpr-framework-wrap">
2
+ <h2>
3
+ <?= __('The GDPR Framework', 'gdpr-admin'); ?>
4
+ </h2>
5
+
6
+ <?php if (!empty($_GET['updated'])) : ?>
7
+ <div id="setting-error-settings_updated" class="updated settings-error notice is-dismissible">
8
+ <p><strong><?php _e('GDPR settings saved!', 'gdpr-admin') ?></strong></p>
9
+ </div>
10
+ <?php endif; ?>
11
+
12
+ <?php if (count($tabs)): ?>
13
+ <nav class="nav-tab-wrapper">
14
+ <?php foreach ($tabs as $slug => $tab): ?>
15
+ <a href="<?= $tab['url']; ?>" class="nav-tab <?= $tab['active'] ? 'nav-tab-active' : ''; ?>">
16
+ <?= $tab['title'] ?>
17
+ </a>
18
+ <?php endforeach; ?>
19
+ </nav>
20
+ <?php endif; ?>
21
+
22
+ <form action="options.php" method="POST">
23
+ <?= $currentTabContents; ?>
24
+ </form>
25
+
26
+ <?php if ($signature): ?>
27
+ <hr>
28
+ <p>
29
+ <em>
30
+ <?= sprintf(
31
+ __('The GDPR Framework. Built with &#9829; by %sCodelight%s.', 'gdpr-admin'),
32
+ '<a href="https://codelight.eu/" target="_blank">',
33
+ '</a>'
34
+ ); ?>
35
+ </em>
36
+ </p>
37
+ <?php endif; ?>
38
+ </div>
views/admin/support/contents.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <hr>
2
+
3
+ <section class="section">
4
+ <h3 class="align-center">
5
+ <?= __('Need more info?', 'gdpr-admin'); ?>
6
+ </h3>
7
+ <div class="row">
8
+ <div class="col">
9
+ <div class="col_image" style="background-image:url('<?= gdpr('config')->get('plugin.url'); ?>/assets/1.png');"></div>
10
+ <a class="button button-primary" href="<?= gdpr('helpers')->docs('wordpress-site-owners-guide-to-gdpr/'); ?>" target="_blank">
11
+ <?= __('Site Owner\'s guide to GDPR', 'gdpr-admin'); ?>
12
+ </a>
13
+ <p>
14
+ <?= __('Read the full guide on GDPR compliance.', 'gdpr-admin'); ?>
15
+ </p>
16
+ </div>
17
+ <div class="col">
18
+ <div class="col_image" style="background-image:url('<?= gdpr('config')->get('plugin.url'); ?>/assets/2.png');"></div>
19
+ <a class="button button-primary" href="<?= gdpr('helpers')->docs('knowledge-base'); ?>" target="_blank">
20
+ <?= __('Knowledge base', 'gdpr-admin'); ?>
21
+ </a>
22
+ <p>
23
+ <?= __('Check out the knowledge base for common questions and answers.', 'gdpr-admin'); ?>
24
+ </p>
25
+ </div>
26
+ <div class="col">
27
+ <div class="col_image" style="background-image:url('<?= gdpr('config')->get('plugin.url'); ?>/assets/3.png');"></div>
28
+ <a class="button button-primary" href="<?= gdpr('helpers')->docs('developer-docs'); ?>" target="_blank">
29
+ <?= __('Developer\'s guide to GDPR', 'gdpr-admin'); ?>
30
+ </a>
31
+ <p>
32
+ <?= __('We have a thorough guide to help making custom sites compliant.', 'gdpr-admin'); ?>
33
+ </p>
34
+ </div>
35
+ </div>
36
+ </section>
37
+
38
+ <section class="section">
39
+ <h3 class="align-center">
40
+ <?= __('Need help?', 'gdpr-admin'); ?>
41
+ </h3>
42
+ <div class="row">
43
+ <div class="col">
44
+ <div class="col_image" style="background-image:url('<?= gdpr('config')->get('plugin.url'); ?>/assets/4.png');"></div>
45
+ <a class="button button-primary" href="https://wordpress.org/support/plugin/gdpr-framework" target="_blank">
46
+ <?= __('Submit a support request', 'gdpr-admin'); ?>
47
+ </a>
48
+ <p>
49
+ <?= __('Found a bug or problem with the plugin? Post in the wordpress.org support forum.', 'gdpr-admin'); ?>
50
+ </p>
51
+ </div>
52
+ <div class="col">
53
+ <div class="col_image" style="background-image:url('<?= gdpr('config')->get('plugin.url'); ?>/assets/5.png');"></div>
54
+ <a class="button button-primary" href="<?= gdpr('helpers')->docs('wordpress-gdpr-consultation'); ?>" target="_blank">
55
+ <?= __('Request a consultation', 'gdpr-admin'); ?>
56
+ </a>
57
+ <p>
58
+ <?= __('Need development or legal assistance in making your site compliant? We can help!', 'gdpr-admin'); ?>
59
+ </p>
60
+ </div>
61
+ </div>
62
+ </section>
views/admin/wizard-buttons.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <a class="button button-primary" href="<?= esc_url($restartUrl); ?>">
2
+ <?= __('Restart setup wizard', 'gdpr-admin'); ?>
3
+ </a>
4
+ <br><br>
5
+ <hr>
views/email/action-export.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <p>
2
+ <?= __(
3
+ sprintf('A data subject (%s) has just downloaded their data in %s format.', esc_html($email), esc_html($format)),
4
+ 'gdpr-admin'
5
+ ); ?>
6
+ </p>
7
+ <p>
8
+ <?= __('This email is just for your information. You don\'t need to take any action', 'gdpr-admin'); ?>
9
+ </p>
views/email/action-forget.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>
2
+ <?= __(
3
+ sprintf('A data subject (%s) has just removed all their data from your website.', esc_html($email)),
4
+ 'gdpr-admin'
5
+ ); ?> <br><br>
6
+
7
+ <?php if ($userId): ?>
8
+ <?= __('The data subject had a user account on your website.'); ?> (ID: <?= $userId; ?>).
9
+ <?php endif; ?>
10
+ </p>
11
+ <p>
12
+ <?= __('This email is just for your information. You don\'t need to take any action', 'gdpr-admin'); ?>
13
+ </p>
views/email/identify-data-subject.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>
2
+ <?= __('Someone has requested access to your data on', 'gdpr'); ?> <?= esc_html($siteName); ?> <br/>
3
+ <?= __('If this was a mistake, just ignore this email and nothing will happen.', 'gdpr'); ?> <br/>
4
+ <?= __('To manage your data, visit the following address:', 'gdpr'); ?> <br/>
5
+ <a href="<?= esc_url($identificationUrl); ?>">
6
+ <?= esc_url($identificationUrl); ?>
7
+ </a>
8
+ </p>
9
+ <p>
10
+ <?= __('This link is valid for 15 minutes.', 'gdpr'); ?>
11
+ </p>
views/email/no-data.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <p>
2
+ <?= __('Someone has requested information about your personal data on', 'gdpr'); ?> <?= esc_html($siteName); ?> <br/>
3
+ <?= __('None of your personal data is stored on', 'gdpr'); ?> <?= esc_html($siteName); ?> <br/>
4
+ <br/>
5
+ <?= __('If this was a mistake or you did not request this email, just ignore it and nothing will happen.', 'gdpr'); ?> <br/>
6
+ </p>
views/email/request-export.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>
2
+ <?= __(
3
+ sprintf('A data subject (%s) has requested to download their data in %s format.', esc_html($email), esc_html($format)),
4
+ 'gdpr-admin'
5
+ ); ?>
6
+ <br>
7
+ <?= __(
8
+ sprintf('To access the data subject\'s data, %sclick here%s', "<a href='{$adminTabLink}'>", '</a>'),
9
+ 'gdpr-admin'
10
+ ); ?>
11
+ </p>
12
+ <p>
13
+ <?= __('As a reminder: according to GDPR, you have 30 days to comply.', 'gdpr-admin'); ?>
14
+ </p>
views/email/request-forget.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>
2
+ <?= __(
3
+ sprintf('A data subject (%s) has requested to remove their data.', esc_html($email)),
4
+ 'gdpr-admin'
5
+ ); ?>
6
+ <br>
7
+ <?= __(
8
+ sprintf('To access the data subject\'s data, %sclick here%s', "<a href='{$adminTabLink}'>", '</a>'),
9
+ 'gdpr-admin'
10
+ ); ?>
11
+ </p>
12
+ <p>
13
+ <?= __('As a reminder: according to GDPR, you have 30 days to comply.', 'gdpr-admin'); ?>
14
+ </p>
views/global/country-options.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <option disabled>-- Choose --</option>
2
+
3
+ <optgroup label="Outside EU">
4
+ <?php foreach ($outside as $code => $name): ?>
5
+ <option
6
+ value="<?= esc_attr($code); ?>"
7
+ <?= selected($code, $current); ?>
8
+ <?php if (in_array($code, ['UK', 'US', 'other'])): ?>
9
+ data-show=".gdpr-representative"
10
+ <?php endif; ?>
11
+ >
12
+ <?= esc_html($name); ?>
13
+ </option>
14
+ <?php endforeach; ?>
15
+ </optgroup>
16
+
17
+ <optgroup label="European Union">
18
+ <?php foreach ($eu as $code => $name): ?>
19
+ <option value="<?= esc_attr($code); ?>" <?= selected($code, $current); ?>>
20
+ <?= esc_html($name); ?>
21
+ </option>
22
+ <?php endforeach; ?>
23
+ </optgroup>
views/global/delete-action.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <option value="anonymize" <?= selected($deleteAction, 'anonymize'); ?>>
2
+ <?= __('Automatically anonymize data', 'gdpr-admin') ?>
3
+ </option>
4
+ <option value="delete" <?= selected($deleteAction, 'delete'); ?> data-show=".js-gdpr-delete-action-reassign">
5
+ <?= __('Automatically delete data', 'gdpr-admin') ?>
6
+ </option>
7
+ <option value="anonymize_and_notify" <?= selected($deleteAction, 'anonymize_and_notify'); ?>
8
+ data-show=".js-gdpr-delete-action-email">
9
+ <?= __('Automatically anonymize data and notify me via email', 'gdpr-admin') ?>
10
+ </option>
11
+ <option value="delete_and_notify" <?= selected($deleteAction, 'delete_and_notify'); ?>
12
+ data-show=".js-gdpr-delete-action-email, .js-gdpr-delete-action-reassign">
13
+ <?= __('Automatically delete data and notify me via email', 'gdpr-admin') ?>
14
+ </option>
15
+ <option value="notify" <?= selected($deleteAction, 'notify'); ?> data-show=".js-gdpr-delete-action-email">
16
+ <?= __('Only notify me via email', 'gdpr-admin') ?>
17
+ </option>
18
+
19
+
20
+
views/global/export-action.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <option value="download" <?= selected($exportAction, 'download'); ?>>
2
+ <?= __('Automatically download data', 'gdpr-admin') ?>
3
+ </option>
4
+ <option value="download_and_notify" <?= selected($exportAction, 'download_and_notify'); ?>
5
+ data-show=".js-gdpr-export-action-email">
6
+ <?= __('Automatically download data and notify me via email', 'gdpr-admin') ?>
7
+ </option>
8
+ <option value="notify" <?= selected($exportAction, 'notify'); ?>
9
+ data-show=".js-gdpr-export-action-email">
10
+ <?= __('Only notify me via email', 'gdpr-admin') ?>
11
+ </option>
12
+
13
+
14
+
views/global/html-data.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en-US">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Your data</title>
6
+
7
+ <style type="text/css">
8
+ table {
9
+ font-family: verdana,arial,sans-serif;
10
+ font-size:11px;
11
+ color:#333333;
12
+ border-width: 1px;
13
+ border-color: #3A3A3A;
14
+ border-collapse: collapse;
15
+ }
16
+ table th {
17
+ border-width: 1px;
18
+ padding: 8px;
19
+ border-style: solid;
20
+ border-color: #3A3A3A;
21
+ background-color: #B3B3B3;
22
+ }
23
+ table td {
24
+ border-width: 1px;
25
+ padding: 8px;
26
+ border-style: solid;
27
+ border-color: #3A3A3A;
28
+ background-color: #ffffff;
29
+ }
30
+
31
+ td.key {
32
+ font-weight: bold;
33
+ }
34
+ </style>
35
+ </head>
36
+ <body>
37
+ <?= $table; ?>
38
+ </body>
39
+ </html>
views/installer/continue-notice.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <p>
2
+ <?= __('The The GDPR Framework setup has not been finalized yet.', 'gdpr-admin'); ?> <br>
3
+ <?= __('You can continue the setup at any time.', 'gdpr-admin'); ?>
4
+ </p>
5
+ <a class="button button-primary" href="<?= $buttonUrl; ?>">
6
+ <?= __('Continue the setup wizard', 'gdpr-admin'); ?>
7
+ </a>
8
+ <a class="button button-secondary" href="<?= $skipUrl; ?>">
9
+ <?= __('Hide this message', 'gdpr-admin'); ?>
10
+ </a>
views/installer/footer.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- Close the installer form -->
2
+ </form>
3
+
4
+ <?php if (!isset($disableBackButton) or !$disableBackButton): ?>
5
+ <form method="POST">
6
+ <input type="hidden" name="gdpr-installer" value="previous" />
7
+ <input type="submit" class="button button-secondary gdpr-step-button gdpr-step-button-prev" value="&laquo; <?= __('Back'); ?>">
8
+ </form>
9
+ <?php endif; ?>
10
+
11
+ </div> <!-- .gdpr-content -->
12
+
13
+
14
+
15
+ <!--
16
+ <div class="gdpr-sticky">
17
+ <hr>
18
+ <a class="button button-primary" href="#">I need help</a>
19
+ </div>
20
+ -->
21
+
22
+ </div> <!-- .container -->
23
+
24
+ <div class="gdpr-footer-links">
25
+ <p>
26
+ You can always leave and continue the setup later from where you left off
27
+ </p>
28
+ <a class="button button-secondary" href="<?= admin_url(); ?>">
29
+ Go to Dashboard
30
+ </a>
31
+ </div>
32
+
33
+ <?php wp_print_scripts([
34
+ 'gdpr-installer',
35
+ 'jquery-repeater',
36
+ 'select2',
37
+ 'conditional-show'
38
+ ]); ?>
39
+ <?php do_action('admin_print_footer_scripts'); ?>
40
+ </body>
41
+ </html>
views/installer/header.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html <?php language_attributes(); ?>>
3
+ <head>
4
+ <meta name="viewport" content="width=device-width" />
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6
+ <title>
7
+ <?php esc_html_e( 'WordPress GDPR &rsaquo; Setup Wizard', 'gdpr-admin' ); ?>
8
+ </title>
9
+ <?php wp_print_scripts(['jquery']); ?>
10
+ <?php do_action('admin_print_styles'); ?>
11
+ <?php do_action('admin_head'); ?>
12
+ </head>
13
+
14
+ <body class="gdpr-installer wp-core-ui">
15
+
16
+ <div class="container gdpr-installer-container">
17
+ <div class="gdpr-header">
18
+ <div class="gdpr-header_left">
19
+ <img class="gdpr-logo" src="<?= gdpr('config')->get('plugin.url'); ?>/assets/gdpr-rhino.svg" />
20
+ </div>
21
+ <div class="gdpr-header_right">
22
+ <h1>
23
+ <?= __('The GDPR Framework', 'gdpr-admin'); ?>
24
+ </h1>
25
+ <a href="<?= gdpr('helpers')->docs('wordpress-gdpr-consultation'); ?>" class="button button-secondary button-side" target="_blank">
26
+ <?= __('I need help', 'gdpr-admin'); ?>
27
+ </a>
28
+ <a href="<?= gdpr('helpers')->docs('developer-docs'); ?>" class="button button-secondary button-side" target="_blank">
29
+ <?= __('Developer Docs', 'gdpr-admin'); ?>
30
+ </a>
31
+ </div>
32
+ </div>
33
+ <div class="gdpr-breadcrumbs">
34
+ <div class="gdpr-breadcrumbs_unit <?= $activeSteps > 0 ? 'active' : ''; ?>">
35
+ <div class="gdpr-breadcrumbs_item">
36
+ <?= __('Configuration', 'gdpr-admin'); ?>
37
+ </div>
38
+ </div>
39
+ <div class="gdpr-breadcrumbs_unit <?= $activeSteps > 1 ? 'active' : ''; ?>">
40
+ <div class="gdpr-breadcrumbs_item">
41
+ <?= __('Privacy Policy', 'gdpr-admin'); ?>
42
+ </div>
43
+ </div>
44
+ <div class="gdpr-breadcrumbs_unit <?= $activeSteps > 2 ? 'active' : ''; ?>">
45
+ <div class="gdpr-breadcrumbs_item">
46
+ <?= __('Forms & Consent', 'gdpr-admin'); ?>
47
+ </div>
48
+ </div>
49
+ <div class="gdpr-breadcrumbs_unit <?= $activeSteps > 3 ? 'active' : ''; ?>">
50
+ <div class="gdpr-breadcrumbs_item">
51
+ <?= __('Integrations', 'gdpr-admin'); ?>
52
+ </div>
53
+ </div>
54
+ </div>
55
+
56
+ <div class="gdpr-content">
57
+
58
+ <?php if (isset($_GET['gdpr-error'])): ?>
59
+ <p class="error">Failed to validate nonce! Please reload page and try again.</p>
60
+ <?php endif; ?>
61
+
62
+ <!-- Open the installer form -->
63
+ <form method="POST">
64
+ <input type="hidden" name="gdpr-installer" value="next" />
views/installer/nonce.php ADDED
@@ -0,0 +1 @@
 
1
+ <input type="hidden" value="<?= $nonce; ?>" name="gdpr_nonce" />
views/installer/steps/configuration-pages.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h1>
2
+ Configuration (1/2)
3
+ </h1>
4
+
5
+ <h2>Privacy Tools page</h2>
6
+ <p>
7
+ The first major requirement of GDPR is that your customers need to be in control of their data. They have the
8
+ right to view, edit and request deleting their <a href="<?= gdpr('helpers')->docs('guide/wordpress-gdpr-definitions-you-need-to-know/#personal-data'); ?>" target="_blank">personal data</a>. Note that this also
9
+ applies to visitors who do not have accounts on your website.
10
+ </p>
11
+ <p>
12
+ For this, we will designate a page where customers will be able to authenticate via login or email and automatically do all of the above.
13
+ <a href="<?= gdpr('helpers')->docs('guide/privacy-tools-page-accessing-exporting-and-deleting-personal-data/'); ?>" target="_blank">Read more about the Privacy Tools page</a>
14
+ </p>
15
+ <hr>
16
+
17
+ <h4>Set up the Privacy Tools page</h4>
18
+ <fieldset>
19
+ <label>
20
+ <input type="radio" name="gdpr_create_tools_page" value="yes" class="js-gdpr-conditional" <?= !$privacyToolsPage ? 'checked': ''; ?>>
21
+ Automatically create a new page for Privacy Tools
22
+ </label>
23
+
24
+ <label>
25
+ <input type="radio" name="gdpr_create_tools_page" value="no" class="js-gdpr-conditional" data-show=".gdpr-select-privacy-tools-page" <?= $privacyToolsPage ? 'checked': ''; ?>> Select an existing page
26
+ </label>
27
+ </fieldset>
28
+
29
+ <p class="gdpr-select-privacy-tools-page hidden">
30
+ <label for="gdpr_tools_page">Select the page for Privacy Tools</label>
31
+ <?= $privacyToolsPageSelector; ?>
32
+ <strong>Important:</strong> Make sure that the page contains the <strong>[gdpr_privacy_tools]</strong> shortcode.
33
+ </p>
34
+
35
+ <hr>
36
+ <br>
37
+ <input type="submit" class="button button-gdpr button-right" value="Save &raquo;"/>
views/installer/steps/configuration-settings.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h1>
2
+ Configuration (2/2)
3
+ </h1>
4
+ <h2>&#10004; Privacy Tools page configured!</h2>
5
+ <p>
6
+ You can take a look at the Privacy Tools page <a href="<?= $privacyToolsPageUrl; ?>" target="_blank">here</a>. <br>
7
+ <br>
8
+ <a href="<?= gdpr('helpers')->docs('guide/privacy-tools-page-accessing-exporting-and-deleting-personal-data/'); ?>" target="_blank">Read more about the Privacy Tools page</a>
9
+ </p>
10
+ <hr>
11
+
12
+ <h2>Right to view & export data</h2>
13
+ <p>
14
+ Your customers have the right to review and export their personal data.
15
+
16
+ <label for="gdpr_export_action">Select what happens if a customer wishes to view or export their personal data</label>
17
+
18
+ <select class="gdpr-select js-gdpr-conditional" name="gdpr_export_action">
19
+ <?= gdpr('view')->render('global/export-action', compact('exportAction')); ?>
20
+ </select>
21
+ <span class="hidden js-gdpr-export-action-email">
22
+ <label for="export_action_email">
23
+ <?= __('Enter the email address to notify', 'gdpr-admin'); ?>
24
+ </label>
25
+ <input
26
+ type="email"
27
+ id="gdpr_export_action_email"
28
+ name="gdpr_export_action_email"
29
+ placeholder="<?= __('Email address', 'gdpr'); ?>"
30
+ value="<?= esc_attr($exportActionEmail); ?>"
31
+ />
32
+ </span>
33
+ </p>
34
+ <hr>
35
+
36
+ <h2>Right to be forgotten</h2>
37
+ <p>
38
+ Your customers have the right to request deleting their personal data.
39
+
40
+ <label for="gdpr_delete_action">Select what happens if a customer wishes to delete their personal data</label>
41
+
42
+ <select class="gdpr-select js-gdpr-conditional" name="gdpr_delete_action">
43
+ <?= gdpr('view')->render('global/delete-action', compact('deleteAction')); ?>
44
+ </select>
45
+
46
+ <span class="hidden js-gdpr-delete-action-reassign">
47
+ <label for="gdpr_delete_action_reassign">If the user has created any content (posts or pages), should it be deleted or reassigned?</label>
48
+ <select id="gdpr_d