1+ <?php
2+
3+ namespace Photobooth \Utility ;
4+
5+ use Photobooth \Utility \PathUtility ;
6+ use Photobooth \Service \LanguageService ;
7+
8+ class CollageLayoutScanner
9+ {
10+ /**
11+ * Scans predefined directories for collage layout JSON files and groups them.
12+ *
13+ * @return array An associative array of grouped collage layouts.
14+ * Example: ['Standard-Layouts' => ['Portrait-Layouts' => [...]], 'Eigene Layouts' => ['Community-Layouts' => [...]]]
15+ */
16+ public static function scanLayouts (): array
17+ {
18+ $ layoutFiles = [];
19+
20+ // Define the main base directories for grouping (e.g., 'template', 'private')
21+ // Use simple keys ('template', 'private') for logical grouping, map to actual paths.
22+ $ mainBaseDirs = [
23+ 'template ' => 'template/collage ' , // Standard layouts path
24+ 'private ' => 'private/collage ' , // User-defined/community layouts path
25+ ];
26+
27+ foreach ($ mainBaseDirs as $ mainGroupKey => $ baseDirRelativePath ) {
28+ $ absoluteBaseDir = PathUtility::getAbsolutePath ($ baseDirRelativePath );
29+
30+ // Initialize the main group key in $layoutFiles early
31+ $ layoutFiles [$ mainGroupKey ] = [];
32+
33+ // Ensure the base directory exists, create if it's a 'private' one and missing
34+ if (!is_dir ($ absoluteBaseDir )) {
35+ if ($ mainGroupKey === 'private ' ) {
36+ try {
37+ mkdir ($ absoluteBaseDir , 0777 , true );
38+ } catch (\Exception $ e ) {
39+ error_log ('CollageLayoutScanner: Failed to create base directory: ' . $ absoluteBaseDir . ' - ' . $ e ->getMessage ());
40+ continue ;
41+ }
42+ } else {
43+ continue ; // Skip if 'template' base dir doesn't exist (expected to be present)
44+ }
45+ }
46+
47+ // --- Scan subdirectories for specific groups (e.g., 'portrait', 'landscape', 'community') ---
48+ $ subDirNames = ['portrait ' , 'landscape ' , 'community ' ]; // Extend as needed
49+
50+ foreach ($ subDirNames as $ subGroupName ) {
51+ $ subDirPath = $ absoluteBaseDir . DIRECTORY_SEPARATOR . $ subGroupName ;
52+
53+ // Ensure the subdirectory exists, create if it's in a 'private' context and missing
54+ if (!is_dir ($ subDirPath )) {
55+ if ($ mainGroupKey === 'private ' ) {
56+ try {
57+ mkdir ($ subDirPath , 0777 , true );
58+ } catch (\Exception $ e ) {
59+ error_log ('CollageLayoutScanner: Failed to create subdirectory: ' . $ subDirPath . ' - ' . $ e ->getMessage ());
60+ continue ;
61+ }
62+ } else {
63+ continue ; // Skip if 'template' subdir doesn't exist (expected to be present)
64+ }
65+ }
66+
67+ // If directory exists (or was created), scan it
68+ // Pass the mainGroupKey AND the subGroupName to build the nested structure
69+ self ::scanDirectory ($ subDirPath , $ layoutFiles [$ mainGroupKey ], $ subGroupName );
70+ }
71+ }
72+
73+ return self ::groupAndTranslateLayouts ($ layoutFiles );
74+ }
75+
76+ /**
77+ * Scans a given directory for JSON files and extracts relevant layout data.
78+ *
79+ * @param string $directory The absolute path to the directory.
80+ * @param array $layoutFiles Reference to the array to store found layouts for the current main group.
81+ * @param string $subGroupKey The key for the subgroup (e.g., 'landscape', 'community', 'square').
82+ */
83+ private static function scanDirectory (string $ directory , array &$ layoutFiles , string $ subGroupKey ): void
84+ {
85+ $ files = glob ($ directory . DIRECTORY_SEPARATOR . '*.json ' );
86+ foreach ($ files as $ filePath ) {
87+ $ fileContent = file_get_contents ($ filePath );
88+ if ($ fileContent === false ) {
89+ error_log ('CollageLayoutScanner: Could not read file: ' . $ filePath );
90+ continue ;
91+ }
92+
93+ $ layoutConfig = json_decode ($ fileContent , true );
94+ if (json_last_error () !== JSON_ERROR_NONE || !is_array ($ layoutConfig )) {
95+ error_log ('CollageLayoutScanner: Malformed JSON in file: ' . $ filePath );
96+ continue ;
97+ }
98+
99+ $ layoutId = basename ($ filePath , '.json ' );
100+
101+ $ layoutName = $ layoutConfig ['name ' ] ?? $ layoutId ;
102+
103+ // Group by the provided $subGroupKey within the main group
104+ // $layoutFiles is passed by reference and already represents $layoutFiles[$mainGroupKey] from scanLayouts
105+ $ layoutFiles [$ subGroupKey ][$ layoutId ] = [
106+ 'id ' => $ layoutId ,
107+ 'name ' => $ layoutName ,
108+ 'description ' => $ layoutConfig ['description ' ] ?? '' ,
109+ 'file_path ' => $ filePath ,
110+ 'author ' => $ layoutConfig ['author ' ] ?? 'Unknown ' ,
111+ 'aspect_ratio ' => $ layoutConfig ['aspect_ratio ' ] ?? '' ,
112+ 'width ' => $ layoutConfig ['width ' ] ?? '' ,
113+ 'height ' => $ layoutConfig ['height ' ] ?? '' ,
114+ ];
115+ }
116+ }
117+
118+ /**
119+ * Groups and translates the found layouts for display without explicit sorting.
120+ *
121+ * @param array $rawLayoutFiles The raw array of found layouts, grouped by main group and subgroup key.
122+ * @return array The grouped layouts, with translated group titles.
123+ */
124+ private static function groupAndTranslateLayouts (array $ rawLayoutFiles ): array
125+ {
126+ $ groupedLayouts = [];
127+ $ languageService = LanguageService::getInstance ();
128+
129+ // Define a desired order and translation keys for the main groups (template, private)
130+ $ mainGroupTranslationKeys = [
131+ 'template ' => 'standard_layouts ' , // e.g., "Standard Layouts"
132+ 'private ' => 'custom_layouts ' , // e.g., "Eigene Layouts"
133+ ];
134+
135+ // Define a desired order and translation keys for the subgroups (portrait, landscape, community)
136+ $ subGroupTranslationKeys = [
137+ 'portrait ' => 'portrait ' ,
138+ 'landscape ' => 'landscape ' ,
139+ 'community ' => 'community_layouts ' ,
140+ // Add other subdir names here
141+ ];
142+
143+ foreach ($ mainGroupTranslationKeys as $ mainGroupKey => $ mainTransKey ) {
144+ $ translatedMainGroupTitle = $ languageService ->translate ($ mainTransKey );
145+ $ groupedLayouts [$ translatedMainGroupTitle ] = []; // Initialize main group
146+
147+ if (isset ($ rawLayoutFiles [$ mainGroupKey ])) {
148+ foreach ($ subGroupTranslationKeys as $ subGroupKey => $ subTransKey ) {
149+ if (isset ($ rawLayoutFiles [$ mainGroupKey ][$ subGroupKey ])) {
150+ $ translatedSubGroupTitle = $ languageService ->translate ($ subTransKey );
151+ // Add directly, no sorting
152+ $ groupedLayouts [$ translatedMainGroupTitle ][$ translatedSubGroupTitle ] = $ rawLayoutFiles [$ mainGroupKey ][$ subGroupKey ];
153+ }
154+ }
155+ // Handle any subgroups not explicitly defined in $subGroupTranslationKeys (e.g., new custom folder)
156+ foreach ($ rawLayoutFiles [$ mainGroupKey ] as $ subGroupKey => $ layouts ) {
157+ if (!array_key_exists ($ subGroupKey , $ subGroupTranslationKeys )) {
158+ $ translatedSubGroupTitle = $ languageService ->translate ($ subGroupKey ); // Try to translate, fallback to key
159+ $ groupedLayouts [$ translatedMainGroupTitle ][$ translatedSubGroupTitle ] = $ layouts ;
160+ }
161+ }
162+ }
163+ }
164+
165+ return $ groupedLayouts ;
166+ }
167+ }
0 commit comments