-
-
Notifications
You must be signed in to change notification settings - Fork 580
/
drupal.go
executable file
·818 lines (688 loc) · 25.6 KB
/
drupal.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
package ddevapp
import (
"fmt"
"github.com/drud/ddev/pkg/appports"
"github.com/drud/ddev/pkg/output"
"github.com/drud/ddev/pkg/util"
"io/ioutil"
"os"
"path"
"path/filepath"
"text/template"
"github.com/drud/ddev/pkg/dockerutil"
"github.com/drud/ddev/pkg/fileutil"
"github.com/drud/ddev/pkg/archive"
)
// DrupalSettings encapsulates all the configurations for a Drupal site.
type DrupalSettings struct {
DeployName string
DeployURL string
DatabaseName string
DatabaseUsername string
DatabasePassword string
DatabaseHost string
DatabaseDriver string
DatabasePort string
DatabasePrefix string
HashSalt string
Signature string
SitePath string
SiteSettings string
SiteSettingsLocal string
SyncDir string
}
// NewDrupalSettings produces a DrupalSettings object with default.
func NewDrupalSettings() *DrupalSettings {
return &DrupalSettings{
DatabaseName: "db",
DatabaseUsername: "db",
DatabasePassword: "db",
DatabaseHost: "db",
DatabaseDriver: "mysql",
DatabasePort: appports.GetPort("db"),
DatabasePrefix: "",
HashSalt: util.RandString(64),
Signature: DdevFileSignature,
SitePath: path.Join("sites", "default"),
SiteSettings: "settings.php",
SiteSettingsLocal: "settings.ddev.php",
SyncDir: path.Join("files", "sync"),
}
}
// DrushConfig encapsulates configuration for a drush settings file.
type DrushConfig struct {
DatabasePort int
DatabaseHost string
}
// NewDrushConfig produces a DrushConfig object with default.
func NewDrushConfig(app *DdevApp) *DrushConfig {
dockerIP, _ := dockerutil.GetDockerIP()
dbPublishedPort, _ := app.GetPublishedPort("db")
return &DrushConfig{
DatabaseHost: dockerIP,
DatabasePort: dbPublishedPort,
}
}
// drupal8SettingsTemplate defines the template that will become a Drupal 8 app's settings.php
// in the event that one does not already exist.
const drupal8SettingsTemplate = `<?php
{{ $config := . }}
// {{ $config.Signature }}: Automatically generated Drupal settings file.
if (file_exists($app_root . '/' . $site_path . '/{{ $config.SiteSettingsLocal }}')) {
include $app_root . '/' . $site_path . '/{{ $config.SiteSettingsLocal }}';
}
`
// drupal8SettingsAppendTemplate defines the template that will be appended to
// a Drupal 8 app's settings.php in the event that one exists.
const drupal8SettingsAppendTemplate = `{{ $config := . }}
// Automatically generated include for settings managed by ddev.
if (file_exists($app_root . '/' . $site_path . '/{{ $config.SiteSettingsLocal }}')) {
include $app_root . '/' . $site_path . '/{{ $config.SiteSettingsLocal }}';
}
`
// drupal7SettingsTemplate defines the template that will become a Drupal 7 app's settings.php
// in the event that one does not already exist.
const drupal7SettingsTemplate = `<?php
{{ $config := . }}
// {{ $config.Signature }}: Automatically generated Drupal settings file.
$ddev_settings = dirname(__FILE__) . '/{{ $config.SiteSettingsLocal }}';
if (is_readable($ddev_settings)) {
require $ddev_settings;
}
`
// drupal7SettingsAppendTemplate defines the template that will be appended to
// a Drupal 7 app's settings.php in the event that one exists.
const drupal7SettingsAppendTemplate = `{{ $config := . }}
// Automatically generated include for settings managed by ddev.
$ddev_settings = dirname(__FILE__) . '/{{ $config.SiteSettingsLocal }}';
if (is_readable($ddev_settings)) {
require $ddev_settings;
}
`
// drupal6SettingsTemplate defines the template that will become a Drupal 6 app's settings.php
// in the event that one does not already exist.
const drupal6SettingsTemplate = drupal7SettingsTemplate
// drupal7SettingsAppendTemplate defines the template that will be appended to
// a Drupal 7 app's settings.php in the event that one exists.
const drupal6SettingsAppendTemplate = drupal7SettingsAppendTemplate
const (
drupal8DdevSettingsTemplate = `<?php
{{ $config := . }}
/**
{{ $config.Signature }}: Automatically generated Drupal settings file.
ddev manages this file and may delete or overwrite the file unless this comment is removed.
*/
$databases['default']['default'] = array(
'database' => "{{ $config.DatabaseName }}",
'username' => "{{ $config.DatabaseUsername }}",
'password' => "{{ $config.DatabasePassword }}",
'host' => "{{ $config.DatabaseHost }}",
'driver' => "{{ $config.DatabaseDriver }}",
'port' => {{ $config.DatabasePort }},
'prefix' => "{{ $config.DatabasePrefix }}",
);
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);
ini_set('session.gc_maxlifetime', 200000);
ini_set('session.cookie_lifetime', 2000000);
$settings['hash_salt'] = '{{ $config.HashSalt }}';
$settings['file_scan_ignore_directories'] = [
'node_modules',
'bower_components',
];
// This will prevent Drupal from setting read-only permissions on sites/default.
$settings['skip_permissions_hardening'] = TRUE;
// This will ensure the site can only be accessed through the intended host names.
// Additional host patterns can be added for custom configurations.
$settings['trusted_host_patterns'] = ['.*'];
// Don't use Symfony's APCLoader. ddev includes APCu; Composer's APCu loader has better performance.
$settings['class_loader_auto_detect'] = FALSE;
// This specifies the default configuration sync directory.
if (empty($config_directories[CONFIG_SYNC_DIRECTORY])) {
$config_directories[CONFIG_SYNC_DIRECTORY] = '{{ joinPath $config.SitePath $config.SyncDir }}';
}
// This determines whether or not drush should include a custom settings file which allows
// it to work both within a docker container and natively on the host system.
$drush_settings = __DIR__ . '/ddev_drush_settings.php';
if (empty(getenv('DDEV_PHP_VERSION')) && file_exists($drush_settings)) {
include $drush_settings;
}
`
)
const (
drupal7DdevSettingsTemplate = `<?php
{{ $config := . }}
/**
{{ $config.Signature }}: Automatically generated Drupal settings file.
ddev manages this file and may delete or overwrite the file unless this comment is removed.
*/
$databases['default']['default'] = array(
'database' => "{{ $config.DatabaseName }}",
'username' => "{{ $config.DatabaseUsername }}",
'password' => "{{ $config.DatabasePassword }}",
'host' => "{{ $config.DatabaseHost }}",
'driver' => "{{ $config.DatabaseDriver }}",
'port' => {{ $config.DatabasePort }},
'prefix' => "{{ $config.DatabasePrefix }}",
);
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);
ini_set('session.gc_maxlifetime', 200000);
ini_set('session.cookie_lifetime', 2000000);
$drupal_hash_salt = '{{ $config.HashSalt }}';
// This determines whether or not drush should include a custom settings file which allows
// it to work both within a docker container and natively on the host system.
$drush_settings = __DIR__ . '/ddev_drush_settings.php';
if (empty(getenv('DDEV_PHP_VERSION')) && file_exists($drush_settings)) {
include $drush_settings;
}
`
)
const (
drupal6DdevSettingsTemplate = `<?php
{{ $config := . }}
/**
{{ $config.Signature }}: Automatically generated Drupal settings file.
ddev manages this file and may delete or overwrite the file unless this comment is removed.
*/
$db_url = '{{ $config.DatabaseDriver }}://{{ $config.DatabaseUsername }}:{{ $config.DatabasePassword }}@{{ $config.DatabaseHost }}:{{ $config.DatabasePort }}/{{ $config.DatabaseName }}';
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);
ini_set('session.gc_maxlifetime', 200000);
ini_set('session.cookie_lifetime', 2000000);
// This determines whether or not drush should include a custom settings file which allows
// it to work both within a docker container and natively on the host system.
$drush_settings = __DIR__ . '/ddev_drush_settings.php';
if (empty(getenv('DDEV_PHP_VERSION')) && file_exists($drush_settings)) {
include $drush_settings;
}
`
)
const drushTemplate = `<?php
{{ $config := . }}
$version = "";
if (defined("\Drupal::VERSION")) {
$version = \Drupal::VERSION;
} else if (defined("VERSION")) {
$version = VERSION;
}
// Use the array format for D7+
if (version_compare($version, "7.0") > 0) {
$databases['default']['default'] = array(
'database' => "db",
'username' => "db",
'password' => "db",
'host' => "{{ $config.DatabaseHost }}",
'driver' => "mysql",
'port' => {{ $config.DatabasePort }},
'prefix' => "",
);
} else {
// or the old db_url format for d6
$db_url = 'mysqli://db:db@{{ $config.DatabaseHost }}:{{ $config.DatabasePort }}/db';
}
`
// manageDrupalSettingsFile will direct inspecting and writing of settings.php.
func manageDrupalSettingsFile(app *DdevApp, drupalConfig *DrupalSettings, settingsTemplate, appendTemplate string) error {
// We'll be writing/appending to the settings files and parent directory, make sure we have permissions to do so
if err := drupalEnsureWritePerms(app); err != nil {
return err
}
if !fileutil.FileExists(app.SiteSettingsPath) {
output.UserOut.Printf("No %s file exists, creating one", drupalConfig.SiteSettings)
if err := writeDrupalSettingsFile(drupalConfig, app.SiteSettingsPath, settingsTemplate); err != nil {
return fmt.Errorf("failed to write: %v", err)
}
}
included, err := settingsHasInclude(drupalConfig, app.SiteSettingsPath)
if err != nil {
return fmt.Errorf("failed to check for include: %v", err)
}
if included {
output.UserOut.Printf("Existing %s file includes %s", drupalConfig.SiteSettings, drupalConfig.SiteSettingsLocal)
} else {
output.UserOut.Printf("Existing %s file does not include %s, modifying to include ddev settings", drupalConfig.SiteSettings, drupalConfig.SiteSettingsLocal)
if err := appendIncludeToDrupalSettingsFile(drupalConfig, app.SiteSettingsPath, appendTemplate); err != nil {
return fmt.Errorf("failed to include %s in %s: %v", drupalConfig.SiteSettingsLocal, drupalConfig.SiteSettings, err)
}
}
return nil
}
// writeDrupalSettingsFile creates the app's settings.php or equivalent,
// which does nothing more than import the ddev-managed settings.ddev.php.
func writeDrupalSettingsFile(drupalConfig *DrupalSettings, filePath string, versionTemplate string) error {
tmpl, err := template.New("settings").Funcs(getTemplateFuncMap()).Parse(versionTemplate)
if err != nil {
return err
}
// Ensure target directory exists and is writable
dir := filepath.Dir(filePath)
if err = os.Chmod(dir, 0755); os.IsNotExist(err) {
if err = os.MkdirAll(dir, 0755); err != nil {
return err
}
} else if err != nil {
return err
}
// Create file
file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
return err
}
defer util.CheckClose(file)
if err := tmpl.Execute(file, drupalConfig); err != nil {
return err
}
return nil
}
// createDrupal7SettingsFile manages creation and modification of settings.php and settings.ddev.php.
// If a settings.php file already exists, it will be modified to ensure that it includes
// settings.ddev.php, which contains ddev-specific configuration.
func createDrupal7SettingsFile(app *DdevApp) (string, error) {
// Currently there isn't any customization done for the drupal config, but
// we may want to do some kind of customization in the future.
drupalConfig := NewDrupalSettings()
if err := manageDrupalSettingsFile(app, drupalConfig, drupal7SettingsTemplate, drupal7SettingsAppendTemplate); err != nil {
return "", err
}
if err := writeDrupal7DdevSettingsFile(drupalConfig, app.SiteLocalSettingsPath); err != nil {
return "", fmt.Errorf("`failed to write` Drupal settings file %s: %v", app.SiteLocalSettingsPath, err)
}
return app.SiteLocalSettingsPath, nil
}
// createDrupal8SettingsFile manages creation and modification of settings.php and settings.ddev.php.
// If a settings.php file already exists, it will be modified to ensure that it includes
// settings.ddev.php, which contains ddev-specific configuration.
func createDrupal8SettingsFile(app *DdevApp) (string, error) {
// Currently there isn't any customization done for the drupal config, but
// we may want to do some kind of customization in the future.
drupalConfig := NewDrupalSettings()
if err := manageDrupalSettingsFile(app, drupalConfig, drupal8SettingsTemplate, drupal8SettingsAppendTemplate); err != nil {
return "", err
}
if err := writeDrupal8DdevSettingsFile(drupalConfig, app.SiteLocalSettingsPath); err != nil {
return "", fmt.Errorf("failed to write Drupal settings file %s: %v", app.SiteLocalSettingsPath, err)
}
return app.SiteLocalSettingsPath, nil
}
// createDrupal6SettingsFile manages creation and modification of settings.php and settings.ddev.php.
// If a settings.php file already exists, it will be modified to ensure that it includes
// settings.ddev.php, which contains ddev-specific configuration.
func createDrupal6SettingsFile(app *DdevApp) (string, error) {
// Currently there isn't any customization done for the drupal config, but
// we may want to do some kind of customization in the future.
drupalConfig := NewDrupalSettings()
// mysqli is required in latest D6LTS and works fine in ddev in old D6
drupalConfig.DatabaseDriver = "mysqli"
if err := manageDrupalSettingsFile(app, drupalConfig, drupal6SettingsTemplate, drupal6SettingsAppendTemplate); err != nil {
return "", err
}
if err := writeDrupal6DdevSettingsFile(drupalConfig, app.SiteLocalSettingsPath); err != nil {
return "", fmt.Errorf("failed to write Drupal settings file %s: %v", app.SiteLocalSettingsPath, err)
}
return app.SiteLocalSettingsPath, nil
}
// writeDrupal8DdevSettingsFile dynamically produces valid settings.ddev.php file by combining a configuration
// object with a data-driven template.
func writeDrupal8DdevSettingsFile(settings *DrupalSettings, filePath string) error {
if fileutil.FileExists(filePath) {
// Check if the file is managed by ddev.
signatureFound, err := fileutil.FgrepStringInFile(filePath, DdevFileSignature)
if err != nil {
return err
}
// If the signature wasn't found, warn the user and return.
if !signatureFound {
util.Warning("%s already exists and is managed by the user.", filepath.Base(filePath))
return nil
}
}
tmpl, err := template.New("settings").Funcs(getTemplateFuncMap()).Parse(drupal8DdevSettingsTemplate)
if err != nil {
return err
}
// Ensure target directory exists and is writable
dir := filepath.Dir(filePath)
if err = os.Chmod(dir, 0755); os.IsNotExist(err) {
if err = os.MkdirAll(dir, 0755); err != nil {
return err
}
} else if err != nil {
return err
}
file, err := os.Create(filePath)
if err != nil {
return err
}
defer util.CheckClose(file)
if err := tmpl.Execute(file, settings); err != nil {
return err
}
return nil
}
// writeDrupal7DdevSettingsFile dynamically produces valid settings.ddev.php file by combining a configuration
// object with a data-driven template.
func writeDrupal7DdevSettingsFile(settings *DrupalSettings, filePath string) error {
if fileutil.FileExists(filePath) {
// Check if the file is managed by ddev.
signatureFound, err := fileutil.FgrepStringInFile(filePath, DdevFileSignature)
if err != nil {
return err
}
// If the signature wasn't found, warn the user and return.
if !signatureFound {
util.Warning("%s already exists and is managed by the user.", filepath.Base(filePath))
return nil
}
}
tmpl, err := template.New("settings").Funcs(getTemplateFuncMap()).Parse(drupal7DdevSettingsTemplate)
if err != nil {
return err
}
// Ensure target directory exists and is writable
dir := filepath.Dir(filePath)
if err = os.Chmod(dir, 0755); os.IsNotExist(err) {
if err = os.MkdirAll(dir, 0755); err != nil {
return err
}
} else if err != nil {
return err
}
file, err := os.Create(filePath)
if err != nil {
return err
}
err = tmpl.Execute(file, settings)
if err != nil {
return err
}
util.CheckClose(file)
return nil
}
// writeDrupal6DdevSettingsFile dynamically produces valid settings.ddev.php file by combining a configuration
// object with a data-driven template.
func writeDrupal6DdevSettingsFile(settings *DrupalSettings, filePath string) error {
if fileutil.FileExists(filePath) {
// Check if the file is managed by ddev.
signatureFound, err := fileutil.FgrepStringInFile(filePath, DdevFileSignature)
if err != nil {
return err
}
// If the signature wasn't found, warn the user and return.
if !signatureFound {
util.Warning("%s already exists and is managed by the user.", filepath.Base(filePath))
return nil
}
}
tmpl, err := template.New("settings").Funcs(getTemplateFuncMap()).Parse(drupal6DdevSettingsTemplate)
if err != nil {
return err
}
// Ensure target directory exists and is writable
dir := filepath.Dir(filePath)
if err = os.Chmod(dir, 0755); os.IsNotExist(err) {
if err = os.MkdirAll(dir, 0755); err != nil {
return err
}
} else if err != nil {
return err
}
file, err := os.Create(filePath)
if err != nil {
return err
}
err = tmpl.Execute(file, settings)
if err != nil {
return err
}
util.CheckClose(file)
return nil
}
// WriteDrushConfig writes out a drush config based on passed-in values.
func WriteDrushConfig(drushConfig *DrushConfig, filePath string) error {
tmpl, err := template.New("drushConfig").Funcs(getTemplateFuncMap()).Parse(drushTemplate)
if err != nil {
return err
}
// Ensure target directory exists and is writable
dir := filepath.Dir(filePath)
if err = os.Chmod(dir, 0755); os.IsNotExist(err) {
if err = os.MkdirAll(dir, 0755); err != nil {
return err
}
} else if err != nil {
return err
}
file, err := os.Create(filePath)
if err != nil {
return err
}
err = tmpl.Execute(file, drushConfig)
if err != nil {
return err
}
util.CheckClose(file)
return nil
}
// getDrupalUploadDir will return a custom upload dir if defined, returning a default path if not.
func getDrupalUploadDir(app *DdevApp) string {
if app.UploadDir == "" {
return "sites/default/files"
}
return app.UploadDir
}
// Drupal8Hooks adds a d8-specific hooks example for post-import-db
const Drupal8Hooks = `
# post-import-db:
# - exec: drush cr
# - exec: drush updb
`
// Drupal7Hooks adds a d7-specific hooks example for post-import-db
const Drupal7Hooks = `
# post-import-db:
# - exec: drush cc all`
// getDrupal7Hooks for appending as byte array
func getDrupal7Hooks() []byte {
return []byte(Drupal7Hooks)
}
// getDrupal6Hooks for appending as byte array
func getDrupal6Hooks() []byte {
// We don't have anything new to add yet, so just use Drupal7 version
return []byte(Drupal7Hooks)
}
// getDrupal8Hooks for appending as byte array
func getDrupal8Hooks() []byte {
return []byte(Drupal8Hooks)
}
// setDrupalSiteSettingsPaths sets the paths to settings.php/settings.ddev.php
// for templating.
func setDrupalSiteSettingsPaths(app *DdevApp) {
drupalConfig := NewDrupalSettings()
settingsFileBasePath := filepath.Join(app.AppRoot, app.Docroot)
app.SiteSettingsPath = filepath.Join(settingsFileBasePath, drupalConfig.SitePath, drupalConfig.SiteSettings)
app.SiteLocalSettingsPath = filepath.Join(settingsFileBasePath, drupalConfig.SitePath, drupalConfig.SiteSettingsLocal)
}
// isDrupal7App returns true if the app is of type drupal7
func isDrupal7App(app *DdevApp) bool {
if _, err := os.Stat(filepath.Join(app.AppRoot, app.Docroot, "misc/ajax.js")); err == nil {
return true
}
return false
}
// isDrupal8App returns true if the app is of type drupal8
func isDrupal8App(app *DdevApp) bool {
if _, err := os.Stat(filepath.Join(app.AppRoot, app.Docroot, "core/scripts/drupal.sh")); err == nil {
return true
}
return false
}
// isDrupal6App returns true if the app is of type Drupal6
func isDrupal6App(app *DdevApp) bool {
if _, err := os.Stat(filepath.Join(app.AppRoot, app.Docroot, "misc/ahah.js")); err == nil {
return true
}
return false
}
// drupal6ConfigOverrideAction overrides php_version for D6, since it is incompatible
// with php7+
func drupal6ConfigOverrideAction(app *DdevApp) error {
app.PHPVersion = PHP56
return nil
}
// drupal8PostStartAction handles default post-start actions for D8 apps, like ensuring
// useful permissions settings on sites/default.
func drupal8PostStartAction(app *DdevApp) error {
if err := createDrupal8SyncDir(app); err != nil {
return err
}
if err := drupalEnsureWritePerms(app); err != nil {
return err
}
// Drush config has to be written after start because we don't know the ports until it's started
drushConfig := NewDrushConfig(app)
err := WriteDrushConfig(drushConfig, filepath.Join(filepath.Dir(app.SiteSettingsPath), "ddev_drush_settings.php"))
if err != nil {
util.Warning("Failed to WriteDrushConfig: %v", err)
}
return nil
}
// drupal7PostStartAction handles default post-start actions for D7 apps, like ensuring
// useful permissions settings on sites/default.
func drupal7PostStartAction(app *DdevApp) error {
if err := drupalEnsureWritePerms(app); err != nil {
return err
}
// Drush config has to be written after start because we don't know the ports until it's started
drushConfig := NewDrushConfig(app)
err := WriteDrushConfig(drushConfig, filepath.Join(filepath.Dir(app.SiteSettingsPath), "ddev_drush_settings.php"))
if err != nil {
util.Warning("Failed to WriteDrushConfig: %v", err)
}
return nil
}
// drupal6PostStartAction handles default post-start actions for D6 apps, like ensuring
// useful permissions settings on sites/default.
func drupal6PostStartAction(app *DdevApp) error {
if err := drupalEnsureWritePerms(app); err != nil {
return err
}
// Drush config has to be written after start because we don't know the ports until it's started
drushConfig := NewDrushConfig(app)
err := WriteDrushConfig(drushConfig, filepath.Join(filepath.Dir(app.SiteSettingsPath), "ddev_drush_settings.php"))
if err != nil {
util.Warning("Failed to WriteDrushConfig: %v", err)
}
return nil
}
// drupalEnsureWritePerms will ensure sites/default and sites/default/settings.php will
// have the appropriate permissions for development.
func drupalEnsureWritePerms(app *DdevApp) error {
output.UserOut.Printf("Ensuring write permissions for %s", app.GetName())
var writePerms os.FileMode = 0200
settingsDir := path.Dir(app.SiteSettingsPath)
makeWritable := []string{
settingsDir,
app.SiteSettingsPath,
app.SiteLocalSettingsPath,
path.Join(settingsDir, "services.yml"),
}
for _, o := range makeWritable {
stat, err := os.Stat(o)
if err != nil {
if !os.IsNotExist(err) {
util.Warning("Unable to ensure write permissions: %v", err)
}
continue
}
if err := os.Chmod(o, stat.Mode()|writePerms); err != nil {
// Warn the user, but continue.
util.Warning("Unable to set permissions: %v", err)
}
}
return nil
}
// createDrupal8SyncDir creates a Drupal 8 app's sync directory
func createDrupal8SyncDir(app *DdevApp) error {
// Currently there isn't any customization done for the drupal config, but
// we may want to do some kind of customization in the future.
drupalConfig := NewDrupalSettings()
syncDirPath := path.Join(app.GetAppRoot(), app.GetDocroot(), drupalConfig.SyncDir)
if fileutil.FileExists(syncDirPath) {
return nil
}
if err := os.MkdirAll(syncDirPath, 0755); err != nil {
return fmt.Errorf("failed to create sync directory (%s): %v", syncDirPath, err)
}
return nil
}
// settingsHasInclude determines if the settings.php or equivalent includes settings.ddev.php or equivalent.
// This is done by looking for the ddev settings file (settings.ddev.php) in settings.php.
func settingsHasInclude(drupalConfig *DrupalSettings, siteSettingsPath string) (bool, error) {
included, err := fileutil.FgrepStringInFile(siteSettingsPath, drupalConfig.SiteSettingsLocal)
if err != nil {
return false, err
}
return included, nil
}
// appendIncludeToDrupalSettingsFile modifies the settings.php file to include the settings.ddev.php
// file, which contains ddev-specific configuration.
func appendIncludeToDrupalSettingsFile(drupalConfig *DrupalSettings, siteSettingsPath string, appendTemplate string) error {
// Check if file is empty
contents, err := ioutil.ReadFile(siteSettingsPath)
if err != nil {
return err
}
// If the file is empty, write the complete settings template and return
if len(contents) == 0 {
return writeDrupalSettingsFile(drupalConfig, siteSettingsPath, appendTemplate)
}
// The file is not empty, open it for appending
file, err := os.OpenFile(siteSettingsPath, os.O_RDWR|os.O_APPEND, 0644)
if err != nil {
return err
}
defer util.CheckClose(file)
tmpl, err := template.New("settings").Funcs(getTemplateFuncMap()).Parse(appendTemplate)
if err != nil {
return err
}
// Write the template to the file
if err := tmpl.Execute(file, drupalConfig); err != nil {
return err
}
return nil
}
// drupalImportFilesAction defines the Drupal workflow for importing project files.
func drupalImportFilesAction(app *DdevApp, importPath, extPath string) error {
destPath := filepath.Join(app.GetAppRoot(), app.GetDocroot(), app.GetUploadDir())
// parent of destination dir should exist
if !fileutil.FileExists(filepath.Dir(destPath)) {
return fmt.Errorf("unable to import to %s: parent directory does not exist", destPath)
}
// parent of destination dir should be writable.
if err := os.Chmod(filepath.Dir(destPath), 0755); err != nil {
return err
}
// If the destination path exists, remove it as was warned
if fileutil.FileExists(destPath) {
if err := os.RemoveAll(destPath); err != nil {
return fmt.Errorf("failed to cleanup %s before import: %v", destPath, err)
}
}
if isTar(importPath) {
if err := archive.Untar(importPath, destPath, extPath); err != nil {
return fmt.Errorf("failed to extract provided archive: %v", err)
}
return nil
}
if isZip(importPath) {
if err := archive.Unzip(importPath, destPath, extPath); err != nil {
return fmt.Errorf("failed to extract provided archive: %v", err)
}
return nil
}
if err := fileutil.CopyDir(importPath, destPath); err != nil {
return err
}
return nil
}