github.com/drud/ddev@v1.21.5-alpha1.0.20230226034409-94fcc4b94453/pkg/ddevapp/backdrop.go (about) 1 package ddevapp 2 3 import ( 4 "fmt" 5 "github.com/drud/ddev/pkg/dockerutil" 6 "github.com/drud/ddev/pkg/nodeps" 7 "os" 8 "path" 9 "path/filepath" 10 "text/template" 11 12 "github.com/drud/ddev/pkg/archive" 13 "github.com/drud/ddev/pkg/fileutil" 14 "github.com/drud/ddev/pkg/output" 15 "github.com/drud/ddev/pkg/util" 16 ) 17 18 // BackdropSettings holds database connection details for Backdrop. 19 type BackdropSettings struct { 20 DatabaseName string 21 DatabaseUsername string 22 DatabasePassword string 23 DatabaseHost string 24 DatabaseDriver string 25 DatabasePort string 26 DatabasePrefix string 27 HashSalt string 28 Signature string 29 SiteSettings string 30 SiteSettingsDdev string 31 DockerIP string 32 DBPublishedPort int 33 } 34 35 // NewBackdropSettings produces a BackdropSettings object with default values. 36 func NewBackdropSettings(app *DdevApp) *BackdropSettings { 37 dockerIP, _ := dockerutil.GetDockerIP() 38 dbPublishedPort, _ := app.GetPublishedPort("db") 39 40 return &BackdropSettings{ 41 DatabaseName: "db", 42 DatabaseUsername: "db", 43 DatabasePassword: "db", 44 DatabaseHost: "ddev-" + app.Name + "-db", 45 DatabaseDriver: "mysql", 46 DatabasePort: GetExposedPort(app, "db"), 47 DatabasePrefix: "", 48 HashSalt: util.RandString(64), 49 Signature: nodeps.DdevFileSignature, 50 SiteSettings: "settings.php", 51 SiteSettingsDdev: "settings.ddev.php", 52 DockerIP: dockerIP, 53 DBPublishedPort: dbPublishedPort, 54 } 55 } 56 57 // createBackdropSettingsFile manages creation and modification of settings.php and settings.ddev.php. 58 // If a settings.php file already exists, it will be modified to ensure that it includes 59 // settings.ddev.php, which contains ddev-specific configuration. 60 func createBackdropSettingsFile(app *DdevApp) (string, error) { 61 settings := NewBackdropSettings(app) 62 63 if !fileutil.FileExists(app.SiteSettingsPath) { 64 output.UserOut.Printf("No %s file exists, creating one", settings.SiteSettings) 65 if err := writeDrupalSettingsPHP(app.SiteSettingsPath, app.Type); err != nil { 66 return "", err 67 } 68 } 69 70 included, err := fileutil.FgrepStringInFile(app.SiteSettingsPath, settings.SiteSettingsDdev) 71 if err != nil { 72 return "", err 73 } 74 75 if included { 76 output.UserOut.Printf("Existing %s includes %s", settings.SiteSettings, settings.SiteSettingsDdev) 77 } else { 78 output.UserOut.Printf("Existing %s file does not include %s, modifying to include ddev settings", settings.SiteSettings, settings.SiteSettingsDdev) 79 80 if err = appendIncludeToDrupalSettingsFile(app.SiteSettingsPath, app.Type); err != nil { 81 return "", fmt.Errorf("failed to include %s in %s: %v", settings.SiteSettingsDdev, settings.SiteSettings, err) 82 } 83 } 84 85 if err = writeBackdropSettingsDdevPHP(settings, app.SiteDdevSettingsFile, app); err != nil { 86 return "", fmt.Errorf("failed to write Drupal settings file %s: %v", app.SiteDdevSettingsFile, err) 87 } 88 89 return app.SiteDdevSettingsFile, nil 90 } 91 92 // writeBackdropSettingsDdevPHP dynamically produces a valid settings.ddev.php file 93 // by combining a configuration object with a data-driven template. 94 func writeBackdropSettingsDdevPHP(settings *BackdropSettings, filePath string, app *DdevApp) error { 95 if fileutil.FileExists(filePath) { 96 // Check if the file is managed by ddev. 97 signatureFound, err := fileutil.FgrepStringInFile(filePath, nodeps.DdevFileSignature) 98 if err != nil { 99 return err 100 } 101 102 // If the signature wasn't found, warn the user and return. 103 if !signatureFound { 104 util.Warning("%s already exists and is managed by the user.", filepath.Base(filePath)) 105 return nil 106 } 107 } 108 t, err := template.New("settings.ddev.php").ParseFS(bundledAssets, path.Join("drupal/backdrop/settings.ddev.php")) 109 if err != nil { 110 return err 111 } 112 113 // Ensure target directory exists and is writable 114 dir := filepath.Dir(filePath) 115 if err = os.Chmod(dir, 0755); os.IsNotExist(err) { 116 if err = os.MkdirAll(dir, 0755); err != nil { 117 return err 118 } 119 } else if err != nil { 120 return err 121 } 122 123 file, err := os.Create(filePath) 124 if err != nil { 125 return err 126 } 127 defer util.CheckClose(file) 128 129 if err := t.Execute(file, settings); err != nil { 130 return err 131 } 132 133 return nil 134 } 135 136 // getBackdropUploadDir will return a custom upload dir if defined, returning a default path if not. 137 func getBackdropUploadDir(app *DdevApp) string { 138 if app.UploadDir == "" { 139 return "files" 140 } 141 142 return app.UploadDir 143 } 144 145 // getBackdropHooks for appending as byte array. 146 func getBackdropHooks() []byte { 147 backdropHooks := `# post-import-db: 148 # - exec: drush cc all 149 ` 150 return []byte(backdropHooks) 151 } 152 153 // setBackdropSiteSettingsPaths sets the paths to settings.php for templating. 154 func setBackdropSiteSettingsPaths(app *DdevApp) { 155 settings := NewBackdropSettings(app) 156 settingsFileBasePath := filepath.Join(app.AppRoot, app.Docroot) 157 app.SiteSettingsPath = filepath.Join(settingsFileBasePath, settings.SiteSettings) 158 app.SiteDdevSettingsFile = filepath.Join(settingsFileBasePath, settings.SiteSettingsDdev) 159 } 160 161 // isBackdropApp returns true if the app is of type "backdrop". 162 func isBackdropApp(app *DdevApp) bool { 163 if _, err := os.Stat(filepath.Join(app.AppRoot, app.Docroot, "core/scripts/backdrop.sh")); err == nil { 164 return true 165 } 166 return false 167 } 168 169 // backdropPostImportDBAction emits a warning about moving configuration into place 170 // appropriately in order for Backdrop to function properly. 171 func backdropPostImportDBAction(app *DdevApp) error { 172 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.") 173 return nil 174 } 175 176 // backdropImportFilesAction defines the Backdrop workflow for importing project files. 177 // The Backdrop workflow is currently identical to the Drupal import-files workflow. 178 func backdropImportFilesAction(app *DdevApp, importPath, extPath string) error { 179 destPath := app.GetHostUploadDirFullPath() 180 181 // parent of destination dir should exist 182 if !fileutil.FileExists(filepath.Dir(destPath)) { 183 return fmt.Errorf("unable to import to %s: parent directory does not exist", destPath) 184 } 185 186 // parent of destination dir should be writable. 187 if err := os.Chmod(filepath.Dir(destPath), 0755); err != nil { 188 return err 189 } 190 191 // If the destination path exists, remove it as was warned 192 if fileutil.FileExists(destPath) { 193 if err := os.RemoveAll(destPath); err != nil { 194 return fmt.Errorf("failed to cleanup %s before import: %v", destPath, err) 195 } 196 } 197 198 if isTar(importPath) { 199 if err := archive.Untar(importPath, destPath, extPath); err != nil { 200 return fmt.Errorf("failed to extract provided archive: %v", err) 201 } 202 203 return nil 204 } 205 206 if isZip(importPath) { 207 if err := archive.Unzip(importPath, destPath, extPath); err != nil { 208 return fmt.Errorf("failed to extract provided archive: %v", err) 209 } 210 211 return nil 212 } 213 214 //nolint: revive 215 if err := fileutil.CopyDir(importPath, destPath); err != nil { 216 return err 217 } 218 219 return nil 220 } 221 222 // backdropPostStartAction handles default post-start actions for backdrop apps, like ensuring 223 // useful permissions settings on sites/default. 224 func backdropPostStartAction(app *DdevApp) error { 225 // Drush config has to be written after start because we don't know the ports until it's started 226 err := WriteDrushrc(app, filepath.Join(filepath.Dir(app.SiteSettingsPath), "drushrc.php")) 227 if err != nil { 228 util.Warning("Failed to WriteDrushrc: %v", err) 229 } 230 231 if _, err = app.CreateSettingsFile(); err != nil { 232 return fmt.Errorf("failed to write settings file %s: %v", app.SiteDdevSettingsFile, err) 233 } 234 return nil 235 }