-
-
Notifications
You must be signed in to change notification settings - Fork 579
/
backdrop.go
356 lines (297 loc) · 11.3 KB
/
backdrop.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
package ddevapp
import (
"fmt"
"github.com/drud/ddev/pkg/dockerutil"
"os"
"path/filepath"
"text/template"
"io/ioutil"
"github.com/drud/ddev/pkg/archive"
"github.com/drud/ddev/pkg/fileutil"
"github.com/drud/ddev/pkg/output"
"github.com/drud/ddev/pkg/util"
)
// BackdropSettings holds database connection details for Backdrop.
type BackdropSettings struct {
DatabaseName string
DatabaseUsername string
DatabasePassword string
DatabaseHost string
DatabaseDriver string
DatabasePort string
DatabasePrefix string
HashSalt string
Signature string
SiteSettings string
SiteSettingsDdev string
DockerIP string
DBPublishedPort int
}
// NewBackdropSettings produces a BackdropSettings object with default values.
func NewBackdropSettings(app *DdevApp) *BackdropSettings {
dockerIP, _ := dockerutil.GetDockerIP()
dbPublishedPort, _ := app.GetPublishedPort("db")
return &BackdropSettings{
DatabaseName: "db",
DatabaseUsername: "db",
DatabasePassword: "db",
DatabaseHost: "db",
DatabaseDriver: "mysql",
DatabasePort: GetPort("db"),
DatabasePrefix: "",
HashSalt: util.RandString(64),
Signature: DdevFileSignature,
SiteSettings: "settings.php",
SiteSettingsDdev: "settings.ddev.php",
DockerIP: dockerIP,
DBPublishedPort: dbPublishedPort,
}
}
// backdropMainSettingsTemplate defines the template that will become settings.php in
// the event that one does not already exist.
const backdropMainSettingsTemplate = `<?php
{{ $config := . }}
// {{ $config.Signature }}: Automatically generated Backdrop settings file.
// Providing these in the main settings file is most likely to match the default config_directories.
$database = 'mysql://user:pass@localhost/database_name';
$config_directories['active'] = 'files/config_' . md5($database) . '/active';
$config_directories['staging'] = 'files/config_' . md5($database) . '/staging';
if (file_exists(__DIR__ . '/{{ $config.SiteSettingsDdev }}')) {
include __DIR__ . '/{{ $config.SiteSettingsDdev }}';
}
`
// backdropSettingsAppendTemplate defines the template that will be appended to
// settings.php in the event that one exists.
const backdropSettingsAppendTemplate = `{{ $config := . }}
// Automatically generated include for settings managed by ddev.
if (file_exists(__DIR__ . '/{{ $config.SiteSettingsDdev }}')) {
include __DIR__ . '/{{ $config.SiteSettingsDdev }}';
}
`
// BackdropDdevSettingsTemplate defines the template that will become settings.ddev.php.
const BackdropDdevSettingsTemplate = `<?php
{{ $config := . }}
/**
{{ $config.Signature }}: Automatically generated Backdrop settings.ddev.php file.
ddev manages this file and may delete or overwrite the file unless this comment is removed.
*/
$host = "{{ $config.DatabaseHost }}";
$port = {{ $config.DatabasePort }};
// If DDEV_PHP_VERSION is not set, it means we're running on the host,
// so use the host-side bind port on docker IP
if (empty(getenv('DDEV_PHP_VERSION'))) {
$host = "{{ $config.DockerIP }}";
$port = {{ $config.DBPublishedPort }};
}
$database = "{{ $config.DatabaseDriver }}://{{ $config.DatabaseUsername }}:{{ $config.DatabasePassword }}@$host:$port/{{ $config.DatabaseName }}";
$database_prefix = '{{ $config.DatabasePrefix }}';
$settings['update_free_access'] = FALSE;
$settings['hash_salt'] = '{{ $config.HashSalt }}';
$settings['backdrop_drupal_compatibility'] = TRUE;
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);
ini_set('session.gc_maxlifetime', 200000);
ini_set('session.cookie_lifetime', 2000000);
`
// createBackdropSettingsFile 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 createBackdropSettingsFile(app *DdevApp) (string, error) {
settings := NewBackdropSettings(app)
if !fileutil.FileExists(app.SiteSettingsPath) {
output.UserOut.Printf("No %s file exists, creating one", settings.SiteSettings)
if err := writeBackdropMainSettingsFile(settings, app.SiteSettingsPath); err != nil {
return "", err
}
}
included, err := fileutil.FgrepStringInFile(app.SiteSettingsPath, settings.SiteSettingsDdev)
if err != nil {
return "", err
}
if included {
output.UserOut.Printf("Existing %s includes %s", settings.SiteSettings, settings.SiteSettingsDdev)
} else {
output.UserOut.Printf("Existing %s file does not include %s, modifying to include ddev settings", settings.SiteSettings, settings.SiteSettingsDdev)
if err = appendIncludeToBackdropSettingsFile(settings, app.SiteSettingsPath); err != nil {
return "", fmt.Errorf("failed to include %s in %s: %v", settings.SiteSettingsDdev, settings.SiteSettings, err)
}
}
if err = writeBackdropDdevSettingsFile(settings, app.SiteDdevSettingsFile); err != nil {
return "", fmt.Errorf("failed to write Drupal settings file %s: %v", app.SiteDdevSettingsFile, err)
}
return app.SiteDdevSettingsFile, nil
}
// writeBackdropMainSettingsFile dynamically produces a valid settings.php file by
// combining a configuration object with a data-driven template.
func writeBackdropMainSettingsFile(settings *BackdropSettings, filePath string) error {
tmpl, err := template.New("settings").Funcs(getTemplateFuncMap()).Parse(backdropMainSettingsTemplate)
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
}
// writeBackdropDdevSettingsFile dynamically produces a valid settings.ddev.php file
// by combining a configuration object with a data-driven template.
func writeBackdropDdevSettingsFile(settings *BackdropSettings, 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(BackdropDdevSettingsTemplate)
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
}
// getBackdropUploadDir will return a custom upload dir if defined, returning a default path if not.
func getBackdropUploadDir(app *DdevApp) string {
if app.UploadDir == "" {
return "files"
}
return app.UploadDir
}
// getBackdropHooks for appending as byte array.
func getBackdropHooks() []byte {
backdropHooks := `
# post-import-db:
# - exec: drush cc all`
return []byte(backdropHooks)
}
// setBackdropSiteSettingsPaths sets the paths to settings.php for templating.
func setBackdropSiteSettingsPaths(app *DdevApp) {
settings := NewBackdropSettings(app)
settingsFileBasePath := filepath.Join(app.AppRoot, app.Docroot)
app.SiteSettingsPath = filepath.Join(settingsFileBasePath, settings.SiteSettings)
app.SiteDdevSettingsFile = filepath.Join(settingsFileBasePath, settings.SiteSettingsDdev)
}
// isBackdropApp returns true if the app is of type "backdrop".
func isBackdropApp(app *DdevApp) bool {
if _, err := os.Stat(filepath.Join(app.AppRoot, app.Docroot, "core/scripts/backdrop.sh")); err == nil {
return true
}
return false
}
// backdropPostImportDBAction emits a warning about moving configuration into place
// appropriately in order for Backdrop to function properly.
func backdropPostImportDBAction(app *DdevApp) error {
util.Warning("Backdrop sites require your config JSON files to be located in your site's \"active\" configuration directory. Please refer to the Backdrop documentation (https://backdropcms.org/user-guide/moving-backdrop-site) for more information about this process.")
return nil
}
// appendIncludeToBackdropSettingsFile modifies the settings.php file to include the settings.ddev.php
// file, which contains ddev-specific configuration.
func appendIncludeToBackdropSettingsFile(settings *BackdropSettings, siteSettingsPath 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 writeBackdropMainSettingsFile(settings, siteSettingsPath)
}
// 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(backdropSettingsAppendTemplate)
if err != nil {
return err
}
// Write the template to the file
if err := tmpl.Execute(file, settings); err != nil {
return err
}
return nil
}
// backdropImportFilesAction defines the Backdrop workflow for importing project files.
// The Backdrop workflow is currently identical to the Drupal import-files workflow.
func backdropImportFilesAction(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
}
// backdropPostStartAction handles default post-start actions for backdrop apps, like ensuring
// useful permissions settings on sites/default.
func backdropPostStartAction(app *DdevApp) error {
// Drush config has to be written after start because we don't know the ports until it's started
err := WriteDrushrc(app, filepath.Join(filepath.Dir(app.SiteSettingsPath), "drushrc.php"))
if err != nil {
util.Warning("Failed to WriteDrushrc: %v", err)
}
if _, err = app.CreateSettingsFile(); err != nil {
return fmt.Errorf("failed to write settings file %s: %v", app.SiteDdevSettingsFile, err)
}
return nil
}