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  }