attentionContext();$r=$c['writer']->recordNativeAttention($c,$id,'native_local_cleanup_pending');$recorded=($r['status']??null)==='native_attention_recorded';}catch(Throwable $e){} if(!$recorded){$message.=' '.__('The attention warning could not be saved. Contact the administrator now.','orderguard');} $message.=self::retainedMessage($retained); return new WP_Error($code,$message,array('attention_recorded'=>$recorded,'retained_orders'=>$retained,'retained_reason'=>'store_policy','erasure_complete'=>false,'automatic_browser_retry'=>false)); } private static function commonRequestEmailError(array $result): WP_Error { $message=__('The erasure request email is used by more than 50 flag records. Automatic deletion was refused; review it as a possible shared placeholder address, then delete this request because it cannot be processed automatically.','orderguard'); if(($result['attention_recorded']??false)!==true){$message.=' '.__('The attention warning could not be saved. Contact the administrator now.','orderguard');} return new WP_Error('native_takeover_request_email_common',$message,array('attention_recorded'=>$result['attention_recorded']??false,'erasure_complete'=>false,'automatic_browser_retry'=>false)); } public static function page($id,$email,$nonce,$page,$drained,OG_Native_Scheduled_Transport $transport,array $keys,string $environment='production',array $history=array(),array $revoked=array()) { if($drained!==true||!is_int($page)||$page<1||$page>1000000){return new WP_Error('native_takeover_not_admitted',__('This cleanup cannot run on this store yet. Nothing was changed.','orderguard'));} $gate=OG_Owned_Native_Fence_Service::request($id,$email,$nonce);if(is_wp_error($gate)){return $gate;} $knownRetained=array(); try{ global $wpdb;$c=(new OG_Owned_WordPress_Context($wpdb))->context();$s=$c['writer']; $pages=$s->readNativePages($c,$id); if(($pages['status']??null)!=='native_pages_state'){throw new RuntimeException('pages');} $n=count($pages['pages']); if($n===0&&!$pages['observed_end']){ $preflight=OG_Native_Local_Flag_Service::preflightRequest($id,$email,$nonce,true); if(is_array($preflight)&&($preflight['reason']??null)==='native_flag_request_email_common'){return self::commonRequestEmailError($preflight);} if(is_wp_error($preflight)||($preflight['status']??null)!=='native_flag_request_admitted'){throw new RuntimeException('flag_preflight');} } if(!$pages['observed_end']||$page<=$n){ $saved=OG_Owned_Native_Page_Capture::request($id,$email,$nonce,$page);if(is_wp_error($saved)){return $saved;} $fence=OG_Owned_Native_Order_Fence_Service::request($id,$email,$nonce,$page);if(is_wp_error($fence)){return $fence;} if(!$saved['observed_end']){return array('items_removed'=>false,'items_retained'=>true,'messages'=>array(__('The order records are kept safe for now. The erasure is not complete.','orderguard')),'done'=>false);} $n=$page; } $operation=$s->prepareNativeRemoteOperation($c,$id); if(($operation['status']??null)!=='native_remote_operation_saved'){throw new RuntimeException('operation');} // Preserve flags and projection BEFORE dispatch/anonymisation; no raw cleanup route. foreach(array('capture'=>'native_flag_manifest_saved','capture_projection'=>'native_projection_captured') as $event=>$expected){ $capture=OG_Native_Local_Flag_Service::captureRequest($id,$email,$nonce,$event,true); if(is_array($capture)&&($capture['reason']??null)==='native_flag_request_email_common'){return self::commonRequestEmailError($capture);} if(is_wp_error($capture)||($capture['status']??null)!==$expected||(($capture['warning']??null)!==null&&($capture['attention_recorded']??false)!==true)){throw new RuntimeException('capture');} } $result=OG_Native_Recovery::run($s,$c,$id,$transport,$keys,$environment,$history,$revoked); $state=$result['result']['outcome']['state']??($result['result']['state']??null); if($state!=='server_committed'){return self::pending($id,'native_remote_pending',__('OrderGuard’s server has not confirmed the erasure yet. No cleanup was started on this store.','orderguard'));} $progress=$s->readNativeLocalProgress($c,$id,$keys,$environment,$history,$revoked); if(($progress['status']??null)!=='native_local_progress'){throw new RuntimeException('progress');} $lastOrderBrowserPage=($n*2)-1; if($page<=$lastOrderBrowserPage){ $cleanupPage=$page-$n+1;if($cleanupPage<1||$cleanupPage>$n){throw new RuntimeException('page');} for($p=1;$p<$cleanupPage;$p++){if(!in_array($p,$progress['verified_pages'],true)){throw new RuntimeException('sequence');}} $cleanup=OG_Native_Cleanup_Coordinator::request($id,$email,$nonce,'orders',$cleanupPage,true,$keys,$environment,$history,$revoked); if(is_array($cleanup)){$knownRetained=$cleanup['retained_orders']??array();} if(is_wp_error($cleanup)||($cleanup['status']??null)!=='native_cleanup_step_finished'){ return self::pending($id,'native_takeover_cleanup_attention',__('The order cleanup needs attention. Part of it may already have happened. The erasure is not complete.','orderguard'),$knownRetained); } $after=$s->readNativeLocalProgress($c,$id,$keys,$environment,$history,$revoked); if(($after['status']??null)!=='native_local_progress'){throw new RuntimeException('progress');}$retained=$after['retained_orders']; return array('items_removed'=>(($cleanup['cleanup']['written_orders']??0)>0||($cleanup['cleanup']['removed_order_metadata']??0)>0),'items_retained'=>true,'messages'=>array(($cleanupPage<$n?__('This order page was processed. More cleanup remains.','orderguard'):__('The orders and their OrderGuard details were cleaned up. Flag cleanup is next.','orderguard')).self::retainedMessage($retained)),'retained_orders'=>$retained,'retained_reason'=>'store_policy','done'=>false); } if(count($progress['verified_pages'])!==$n){throw new RuntimeException('sequence');}$retained=$progress['retained_orders']; $plan=$s->readNativeLocalFlagPlan($c,$id);if(($plan['status']??null)!=='native_flag_plan'){throw new RuntimeException('flag_plan');} $offset=$page-($lastOrderBrowserPage+1);$deletePages=$plan['delete_pages'];$linkPages=$plan['link_pages']; $projectionOffset=$deletePages+$linkPages; if($offset<=$projectionOffset){ if($offset<$deletePages){$event='delete';$step=$offset+1;$message=__('A batch of flags was cleaned up. More cleanup remains.','orderguard');} elseif($offset<$projectionOffset){$event='detach_links';$step=$offset-$deletePages+1;$message=__('A batch of links between flags and orders was cleaned up. More cleanup remains.','orderguard');} else{$event='apply_projection';$step=1;$message=__('The flags were cleaned up. Old saved copies are next.','orderguard');} $cleanup=OG_Native_Cleanup_Coordinator::request($id,$email,$nonce,$event,$step,true,$keys,$environment,$history,$revoked); if(is_wp_error($cleanup)||($cleanup['status']??null)!=='native_cleanup_step_finished'){ return self::pending($id,'native_takeover_cleanup_attention',__('The flag cleanup needs attention. Part of it may already have happened. The erasure is not complete.','orderguard'),$retained); } if($event==='apply_projection'){return array('items_removed'=>!($cleanup['cleanup']['replayed']??false),'items_retained'=>true,'messages'=>array($message.self::retainedMessage($retained)),'retained_orders'=>$retained,'retained_reason'=>'store_policy','done'=>false);} $changed=(int)($cleanup['cleanup'][$event==='delete'?'deleted':'detached']??0)>0; return array('items_removed'=>$changed,'items_retained'=>true,'messages'=>array($message.self::retainedMessage($retained)),'retained_orders'=>$retained,'retained_reason'=>'store_policy','done'=>false); } $legacy=$s->readNativeTakeoverProgress($c,$id);if(($legacy['status']??null)!=='native_takeover_progress'){throw new RuntimeException('legacy_progress_'.($legacy['reason']??'unknown'));} if($legacy['stage']==='finished'){return array('items_removed'=>false,'items_retained'=>false,'messages'=>array(__('OrderGuard personal data was removed and its local completion checks passed.','orderguard').self::retainedMessage($retained)),'retained_orders'=>$retained,'retained_reason'=>'store_policy','done'=>true);} $event=$legacy['stage'];$cursor=$legacy['cursor'];$cleanup=OG_Native_Cleanup_Coordinator::request($id,$email,$nonce,$event,$cursor,true,$keys,$environment,$history,$revoked); if(is_array($cleanup)&&($cleanup['status']??null)==='native_cleanup_step_pending'){ return array('items_removed'=>false,'items_retained'=>true,'messages'=>array(__('OrderGuard is waiting for a network signal to be withdrawn. The erasure is not complete yet.','orderguard').self::retainedMessage($retained)),'retained_orders'=>$retained,'retained_reason'=>'store_policy','done'=>false); } if(is_wp_error($cleanup)||($cleanup['status']??null)!=='native_cleanup_step_finished'){return self::pending($id,'native_takeover_cleanup_attention',__('The cleanup of old saved copies needs attention. Part of it may already have happened. The erasure is not complete.','orderguard'),$retained);} $advanced=$s->advanceNativeTakeoverProgress($c,$id,$event,$cursor,$cleanup['cleanup']);if(($advanced['status']??null)!=='native_takeover_progress_advanced'){throw new RuntimeException('legacy_progress_'.($advanced['reason']??'unknown'));} $changed=(int)($cleanup['cleanup']['deleted_rows']??0)>0||(int)($cleanup['cleanup']['evicted_entries']??0)>0; if($advanced['stage']==='finished'){return array('items_removed'=>$changed,'items_retained'=>false,'messages'=>array(__('OrderGuard personal data was removed and its local completion checks passed.','orderguard').self::retainedMessage($retained)),'retained_orders'=>$retained,'retained_reason'=>'store_policy','done'=>true);} $message=$advanced['stage']==='signal_outbox'?__('Old saved copies were cleaned up. Network signal cleanup is next.','orderguard'):($event==='signal_outbox'?__('A batch of network signal records was cleaned up. More cleanup remains.','orderguard'):__('A batch of old saved copies was cleaned up. More cleanup remains.','orderguard')); return array('items_removed'=>$changed,'items_retained'=>true,'messages'=>array($message.self::retainedMessage($retained)),'retained_orders'=>$retained,'retained_reason'=>'store_policy','done'=>false); }catch(Throwable $e){return self::pending($id,'native_takeover_attention',__('The next cleanup step could not be verified. Erasure is incomplete.','orderguard'),$knownRetained);} } }