@@ -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,124 @@ 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 ()) {
121- // deprecated, remove in v13
122- $ this ->eventDispatcher ->dispatch (new PublishingOfOneRecordBegan ($ record ));
123- if ($ record ->getState () !== Record::S_UNCHANGED ) {
124- $ this ->publisherCollection ->publish ($ record );
125- }
126- // deprecated, remove in v13
127- $ this ->eventDispatcher ->dispatch (new PublishingOfOneRecordEnded ($ record ));
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 ) {
170+ $ this ->publisherCollection ->publish ($ record );
128171 $ this ->eventDispatcher ->dispatch (new RecordWasPublished ($ record ));
172+
173+ return true ;
129174 }
130175
176+ return false ;
177+ }
178+
179+ private function shouldRecordBePublished (Record $ record , bool $ isTopLevelCall ): bool
180+ {
181+ // If not in publishAll mode, use the less strict check
182+ if (!$ this ->isPublishAllMode ) {
183+ return true ;
184+ }
185+
186+ if ($ isTopLevelCall && $ record ->getClassification () === 'pages ' ) {
187+ // Top-level pages and independent child pages must pass strict dependency check
188+ return $ record ->isPublishable ();
189+ }
190+
191+ // For all other cases in publishAll mode use the less strict check since parent context provides validity
192+ // - Child pages whose parent was published in current run
193+ // - Non-page records (content elements, etc.)
194+ // - Translations
195+ return true ;
196+ }
197+
198+ private function processTranslations (Record $ record , bool $ includeChildPages , bool $ parentWasPublished = false ): void
199+ {
131200 $ translationEvent = new BeforePublishingTranslationsEvent ($ record , $ includeChildPages );
132201 $ this ->eventDispatcher ->dispatch ($ translationEvent );
133202
134- if ($ translationEvent ->shouldProcessTranslations ()) {
135- foreach ($ record ->getTranslations () as $ translatedRecords ) {
136- foreach ($ translatedRecords as $ translatedRecord ) {
137- $ this ->publishRecord ($ translatedRecord , $ includeChildPages );
138- }
203+ if (!$ translationEvent ->shouldProcessTranslations ()) {
204+ return ;
205+ }
206+
207+ foreach ($ record ->getTranslations () as $ translatedRecords ) {
208+ foreach ($ translatedRecords as $ translatedRecord ) {
209+ // Translations are never top-level calls
210+ $ this ->publishRecord ($ translatedRecord , $ includeChildPages , false );
211+ }
212+ }
213+ }
214+
215+ private function processChildPagesIndependently (Record $ record , bool $ includeChildPages ): void
216+ {
217+ foreach ($ record ->getChildren () as $ table => $ children ) {
218+ if ($ table !== 'pages ' ) {
219+ continue ; // Only process child pages independently
220+ }
221+
222+ foreach ($ children as $ child ) {
223+ // Child pages evaluated independently with strict checking
224+ $ this ->publishRecord ($ child , $ includeChildPages , true );
139225 }
140226 }
227+ }
141228
229+ private function processChildRecords (Record $ record , bool $ includeChildPages , bool $ parentWasPublished = false ): void
230+ {
142231 foreach ($ record ->getChildren () as $ table => $ children ) {
143- if ('pages ' === $ table && !$ includeChildPages ) {
144- continue ;
232+ // Handle child pages based on mode and settings
233+ if ($ table === 'pages ' ) {
234+ if (!$ includeChildPages && !$ this ->isPublishAllMode ) {
235+ // Skip child pages only if not including them AND not in publishAll mode
236+ continue ;
237+ }
145238 }
239+
146240 foreach ($ children as $ child ) {
147- $ this ->publishRecord ($ child , $ includeChildPages );
241+ // Determine if child should be treated as top-level for dependency checking
242+ $ isChildTopLevelCall = false ;
243+
244+ if ($ table === 'pages ' ) {
245+ if ($ this ->isPublishAllMode ) {
246+ // In publishAll mode: child pages get strict checking ONLY if their parent
247+ // was not published in the current run (independent evaluation)
248+ $ isChildTopLevelCall = !$ parentWasPublished ;
249+ } elseif ($ includeChildPages && !$ this ->isPublishAllMode ) {
250+ // Child pages in regular mode with includeChildPages always get strict checking
251+ $ isChildTopLevelCall = true ;
252+ }
253+ }
254+
255+ $ this ->publishRecord ($ child , $ includeChildPages , $ isChildTopLevelCall );
148256 }
149257 }
150258 }
151- }
259+ }
0 commit comments