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