@@ -33,6 +33,7 @@ class PublisherService
3333 protected PublisherCollection $ publisherCollection ;
3434 /** @var array<string, array<int, true>> */
3535 protected array $ visitedRecords = [];
36+ protected bool $ isPublishAllMode = false ;
3637
3738 public function __construct ()
3839 {
@@ -56,6 +57,20 @@ public function publish(PublishingContext $publishingContext): void
5657 $ this ->publishRecordTree ($ recordTree , $ publishingContext ->publishChildPages );
5758 }
5859
60+ /**
61+ * Publishes all PUBLISHABLE records (dependencies must NOT be ignored during publishAll)
62+ * @throws Throwable
63+ */
64+ public function publishAllPublishable (PublishingContext $ publishingContext ): void
65+ {
66+ $ this ->isPublishAllMode = true ;
67+ try {
68+ $ this ->publish ($ publishingContext );
69+ } finally {
70+ $ this ->isPublishAllMode = false ;
71+ }
72+ }
73+
5974 /**
6075 * @throws Throwable
6176 * @internal This method will be made non-public in in2publish_core v13. Use publish() with PublishingContext
@@ -76,10 +91,13 @@ public function publishRecordTree(RecordTree $recordTree, bool $includeChildPage
7691
7792 $ this ->publisherCollection ->start ();
7893
94+ // this is set for the first call to publishRecord, because
95+ $ isTopLevelCall = true ;
96+
7997 try {
8098 foreach ($ recordTree ->getChildren () as $ records ) {
8199 foreach ($ records as $ record ) {
82- $ this ->publishRecord ($ record , $ includeChildPages );
100+ $ this ->publishRecord ($ record , $ includeChildPages, $ isTopLevelCall );
83101 }
84102 }
85103 } catch (Throwable $ exception ) {
@@ -103,8 +121,11 @@ public function publishRecordTree(RecordTree $recordTree, bool $includeChildPage
103121 /**
104122 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
105123 */
106- protected function publishRecord (Record $ record , bool $ includeChildPages = false ): void
107- {
124+ protected function publishRecord (
125+ Record $ record ,
126+ bool $ includeChildPages = false ,
127+ bool $ isTopLevelCall = false
128+ ): void {
108129 $ classification = $ record ->getClassification ();
109130 $ id = $ record ->getId ();
110131
@@ -115,37 +136,130 @@ protected function publishRecord(Record $record, bool $includeChildPages = false
115136
116137 $ this ->eventDispatcher ->dispatch (new RecordWasSelectedForPublishing ($ record ));
117138
118- // Do not use Record::isPublishable(). Check only the record's reasons but not dependencies.
119- // Dependencies might have been fulfilled during publishing or ignored by the user by choice.
120- if (!$ record ->hasReasonsWhyTheRecordIsNotPublishable ()) {
139+ $ wasPublished = $ this ->publishRecordIfPublishable ($ record , $ isTopLevelCall );
140+
141+ // Always process children if record was published
142+ // Also process children if we're not in publishAll mode (individual publishing)
143+ // In publishAll mode: only process child pages if includeChildPages is true AND parent was published
144+ // Always process non-page children if parent was published
145+ $ shouldProcessChildren = $ wasPublished || !$ this ->isPublishAllMode ;
146+
147+ if ($ shouldProcessChildren ) {
148+ $ this ->processTranslations ($ record , $ includeChildPages , $ wasPublished );
149+ $ this ->processChildRecords ($ record , $ includeChildPages , $ wasPublished );
150+ } elseif ($ this ->isPublishAllMode && $ includeChildPages ) {
151+ // in publishAll mode child pages need to be evaluated independently even if parent wasn't published
152+ $ this ->processChildPagesIndependently ($ record , $ includeChildPages );
153+ }
154+ }
155+
156+ private function publishRecordIfPublishable (Record $ record , bool $ isTopLevelCall ): bool
157+ {
158+ if ($ record ->hasReasonsWhyTheRecordIsNotPublishable ()) {
159+ return false ;
160+ }
161+
162+ if ($ record ->getStateRecursive () === Record::S_UNCHANGED ) {
163+ return false ;
164+ }
165+
166+ // Determine if record is publishable based on context
167+ $ shouldPublish = $ this ->shouldRecordBePublished ($ record , $ isTopLevelCall );
168+
169+ if ($ shouldPublish ) {
121170 // deprecated, remove in v13
122171 $ this ->eventDispatcher ->dispatch (new PublishingOfOneRecordBegan ($ record ));
123- if ( $ record -> getState () !== Record:: S_UNCHANGED ) {
124- $ this ->publisherCollection ->publish ($ record );
125- }
172+
173+ $ this ->publisherCollection ->publish ($ record );
174+
126175 // deprecated, remove in v13
127176 $ this ->eventDispatcher ->dispatch (new PublishingOfOneRecordEnded ($ record ));
128177 $ this ->eventDispatcher ->dispatch (new RecordWasPublished ($ record ));
178+
179+ return true ;
129180 }
130181
182+ return false ;
183+ }
184+
185+ private function shouldRecordBePublished (Record $ record , bool $ isTopLevelCall ): bool
186+ {
187+ // If not in publishAll mode, use the less strict check
188+ if (!$ this ->isPublishAllMode ) {
189+ return true ;
190+ }
191+
192+ if ($ isTopLevelCall && $ record ->getClassification () === 'pages ' ) {
193+ // Top-level pages and independent child pages must pass strict dependency check
194+ return $ record ->isPublishable ();
195+ }
196+
197+ // For all other cases in publishAll mode use the less strict check since parent context provides validity
198+ // - Child pages whose parent was published in current run
199+ // - Non-page records (content elements, etc.)
200+ // - Translations
201+ return true ;
202+ }
203+
204+ private function processTranslations (Record $ record , bool $ includeChildPages , bool $ parentWasPublished = false ): void
205+ {
131206 $ translationEvent = new BeforePublishingTranslationsEvent ($ record , $ includeChildPages );
132207 $ this ->eventDispatcher ->dispatch ($ translationEvent );
133208
134- if ($ translationEvent ->shouldProcessTranslations ()) {
135- foreach ($ record ->getTranslations () as $ translatedRecords ) {
136- foreach ($ translatedRecords as $ translatedRecord ) {
137- $ this ->publishRecord ($ translatedRecord , $ includeChildPages );
138- }
209+ if (!$ translationEvent ->shouldProcessTranslations ()) {
210+ return ;
211+ }
212+
213+ foreach ($ record ->getTranslations () as $ translatedRecords ) {
214+ foreach ($ translatedRecords as $ translatedRecord ) {
215+ // Translations are never top-level calls
216+ $ this ->publishRecord ($ translatedRecord , $ includeChildPages , false );
139217 }
140218 }
219+ }
141220
221+ private function processChildPagesIndependently (Record $ record , bool $ includeChildPages ): void
222+ {
142223 foreach ($ record ->getChildren () as $ table => $ children ) {
143- if (' pages ' === $ table && ! $ includeChildPages ) {
144- continue ;
224+ if ($ table !== ' pages ' ) {
225+ continue ; // Only process child pages independently
145226 }
227+
146228 foreach ($ children as $ child ) {
147- $ this ->publishRecord ($ child , $ includeChildPages );
229+ // Child pages evaluated independently with strict checking
230+ $ this ->publishRecord ($ child , $ includeChildPages , true );
231+ }
232+ }
233+ }
234+
235+ private function processChildRecords (Record $ record , bool $ includeChildPages , bool $ parentWasPublished = false ): void
236+ {
237+ foreach ($ record ->getChildren () as $ table => $ children ) {
238+ // Handle child pages based on mode and settings
239+ if ($ table === 'pages ' ) {
240+ if (!$ includeChildPages && !$ this ->isPublishAllMode ) {
241+ // Skip child pages only if not including them AND not in publishAll mode
242+ continue ;
243+ }
244+ }
245+
246+ foreach ($ children as $ child ) {
247+ // Determine if child should be treated as top-level for dependency checking
248+ $ isChildTopLevelCall = false ;
249+
250+ if ($ table === 'pages ' ) {
251+ if ($ this ->isPublishAllMode ) {
252+ // In publishAll mode: child pages get strict checking ONLY if their parent
253+ // was not published in the current run (independent evaluation)
254+ $ isChildTopLevelCall = !$ parentWasPublished ;
255+ } elseif ($ includeChildPages && !$ this ->isPublishAllMode ) {
256+ // Child pages in regular mode with includeChildPages always get strict checking
257+ $ isChildTopLevelCall = true ;
258+ }
259+ }
260+
261+ $ this ->publishRecord ($ child , $ includeChildPages , $ isChildTopLevelCall );
148262 }
149263 }
150264 }
151- }
265+ }
0 commit comments