monster_menus-9.0.x-dev/src/GetTreeIterator/MMExportIter.php
src/GetTreeIterator/MMExportIter.php
<?php
namespace Drupal\monster_menus\GetTreeIterator;
use Drupal;
use Drupal\content_sync\ContentSyncManager;
use Drupal\content_sync\Exporter\ContentExporter;
use Drupal\Core\Database\Database;
use Drupal\Core\Serialization\Yaml;
use Drupal\monster_menus\Constants;
use Drupal\monster_menus\GetTreeIterator;
use Drupal\monster_menus\ImportExport;
use Drupal\monster_menus\MMCreatePath\MMCreatePathGroup;
use Drupal\monster_menus\MMCreatePath\MMCreatePathCat;
use Drupal\monster_menus\MMImportExportException;
use Drupal\node\Entity\Node;
use Drupal\node\NodeInterface;
use Drupal\user\Entity\User;
use Symfony\Component\Yaml\Dumper;
class MMExportIter extends GetTreeIterator {
/**
* Database Service Object.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* Content sync service.
*
* @var ContentExporter
*/
protected $contentExporter;
/**
* Content sync service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
public $nodes, $pages, $subrequest, $pool, $users, $parents, $aliases;
private $include_nodes, $page_nodes, $dependencies;
/**
* Constructs an MMExportIter object.
*
* @param bool $include_nodes
* If TRUE, include node contents.
*/
public function __construct($include_nodes) {
$this->pages = $this->nodes = $this->parents = $this->aliases = $this->subrequest = $this->pool = $this->users = $this->page_nodes = $this->dependencies = [];
$this->include_nodes = $include_nodes;
$this->database = Database::getConnection();
$this->entityTypeManager = Drupal::entityTypeManager();
if ($include_nodes) {
$this->contentExporter = \Drupal::service('content_sync.exporter');
}
}
/**
* {@inheritdoc}
*/
public function iterate($item) {
// Skip recycle bins
if ($item->perms[Constants::MM_PERMS_IS_RECYCLE_BIN]) {
return -1;
}
if (!$item->perms[Constants::MM_PERMS_READ]) {
throw new MMImportExportException('You do not have permission to read the page/group with mmtid=@mmtid.', ['@mmtid' => $item->mmtid]);
}
// Create a new object and filter out unused fields.
$compare = $item->perms[Constants::MM_PERMS_IS_GROUP] ? new MMCreatePathGroup([]) : new MMCreatePathCat([]);
$i = (object)array_intersect_key((array)$item, (array)$compare);
unset($i->mmtid);
if (!empty($item->bid)) {
$i->menu_start = $item->bid;
}
$i->perms = [];
// Groups
$select = $this->database->select('mm_tree', 't');
$select->join('mm_tree_access', 'a', 'a.mmtid = t.mmtid');
$select->leftJoin('mm_tree', 't2', 'a.gid = t2.mmtid');
$result = $select->fields('t2', ['mmtid', 'name'])
->fields('a', ['mode'])
->condition('t2.mmtid', '0', '>=')
->condition('a.mmtid', $item->mmtid)
->execute();
foreach ($result as $r) {
if ($r->mmtid == $item->mmtid) {
$i->perms[$r->mode]['groups'][] = 'self';
}
else {
$i->perms[$r->mode]['groups'][$r->mmtid] = $this->addGroup($r->mmtid, $item->mmtid);
}
}
// Individual users
$select = $this->database->select('mm_tree', 't');
$select->join('mm_tree_access', 'a', 'a.mmtid = t.mmtid');
$result = $select->fields('a', ['mode', 'gid'])
->condition('a.gid', '0', '<')
->condition('a.mmtid', $item->mmtid)
->execute();
foreach ($result as $r) {
$i->perms[$r->mode]['users'] = [];
foreach (mm_content_get_uids_in_group($r->gid) as $uid) {
$i->perms[$r->mode]['users'][] = $this->uid2username($uid);
}
}
// Owner
$i->uid = $this->uid2username($i->uid);
if ($item->perms[Constants::MM_PERMS_IS_GROUP]) {
if ($i->vgroup = mm_content_is_vgroup($item->mmtid)) {
// Virtual group
$vquery = $this->database->query('SELECT qfrom, field FROM {mm_group} g INNER JOIN {mm_vgroup_query} v ON g.vgid = v.vgid WHERE g.gid = :mmtid', [':mmtid' => $item->mmtid])->fetchObject();
if ($vquery) {
$i->qfrom = $vquery->qfrom;
$i->qfield = $vquery->field;
}
}
else {
// Regular group
$i->members = [];
foreach (mm_content_get_uids_in_group($item->mmtid) as $uid) {
$i->members[] = $this->uid2username($uid);
}
}
}
else {
// Cascaded settings
$result = $this->database->query('SELECT * FROM {mm_cascaded_settings} WHERE mmtid = :mmtid', [':mmtid' => $item->mmtid]);
foreach ($result as $r) {
if ($r->multiple) {
if (!isset($i->cascaded) || empty($i->cascaded[$r->name])) {
$i->cascaded[$r->name] = [];
}
$i->cascaded[$r->name][] = $r->data;
}
else {
$i->cascaded[$r->name] = $r->data;
}
}
// Nodes
if ($this->include_nodes) {
if ($nids = mm_content_get_nids_by_mmtid($item->mmtid)) {
foreach (array_diff($nids, array_keys($this->nodes)) as $new_nid) {
$this->nodes[$new_nid] = TRUE;
}
// Convert string nids to integers, to help shorten output.
$this->page_nodes[$item->mmtid] = array_map(fn($nid) => (int) $nid, $nids);
}
}
}
$this->pool[$item->mmtid] = $item->perms[Constants::MM_PERMS_IS_GROUP] ? new MMCreatePathGroup((array) $i) : new MMCreatePathCat((array) $i);
if (!$this->subrequest) {
array_splice($this->aliases, $item->level);
$this->aliases[] = $item->alias;
array_splice($this->parents, $item->level);
$this->parents[] = (int) $item->mmtid;
$this->pages[join('/', $this->aliases)] = $this->parents;
}
return 1;
}
public function dump() {
$out = 'version: ' . ImportExport::MM_IMPORT_VERSION . "\n\n";
if ($this->nodes) {
$out .= "nodes:\n";
$serializer_context = ['content_sync_file_base_64' => TRUE];
$language = \Drupal::languageManager()->getCurrentLanguage()->getId();
foreach (array_keys($this->nodes) as $nid) {
/** @var NodeInterface $node */
if ($node = Node::load($nid)) {
unset($node->mm_catlist);
if ($node->__get('users_w')) {
$new_users = [];
foreach (array_keys($node->__get('users_w')) as $uid) {
$new_users[$this->uid2username($uid)] = '';
}
$node->__set('users_w', $new_users);
}
if ($node->__get('groups_w')) {
$groups_w = [];
foreach (array_keys($node->__get('groups_w')) as $gid) {
$groups_w[$gid] = $this->addGroup($gid);
}
$node->__set('groups_w', $groups_w);
}
$node = $node->getTranslation($language);
$exported_node_yaml = trim($this->contentExporter->exportEntity($node, $serializer_context));
$exported_node_yaml = $this->addDependencies($exported_node_yaml, $serializer_context);
$out .= " $nid:\n " . str_replace("\n", "\n ", $exported_node_yaml) . "\n";
}
}
$out .= "page_nodes:\n";
foreach ($this->pool as $mmtid => $item) {
if (isset($this->page_nodes[$mmtid])) {
$out .= " $mmtid:\n" . $this->exportVar($this->page_nodes[$mmtid], ' ');
}
}
if ($this->dependencies) {
$out .= "\ndependencies:\n" . join('', $this->dependencies);
}
}
else {
$out .= "nodes: {}\n\npage_nodes: {}\n";
}
$out .= "pages:\n";
$out .= $this->exportVar($this->pages, ' ');
$out .= "\npool:\n";
foreach ($this->pool as $mmtid => $item) {
$out .= " $mmtid:\n" . $this->exportVar($item, ' ');
}
return $out;
}
private function addDependencies($exported_yaml, $serializer_context) {
$exported_entity = Yaml::decode($exported_yaml);
if (isset($exported_entity['_content_sync']['entity_dependencies'])) {
foreach ($exported_entity['_content_sync']['entity_dependencies'] as $type => $depends) {
if (!in_array($type, ['user', 'node', 'mm_tree'])) {
foreach ($depends as $depend) {
if (!isset($this->dependencies[$depend])) {
[$type, , $uuid] = explode(ContentSyncManager::DELIMITER, $depend);
if ($entities = $this->entityTypeManager->getStorage($type)->loadByProperties(['uuid' => $uuid])) {
// Prevent the recursive call to this function from exporting
// the dependency again.
$this->dependencies[$depend] = TRUE;
$exported_yaml = trim($this->contentExporter->exportEntity(reset($entities), $serializer_context));
$exported_yaml = $this->addDependencies($exported_yaml, $serializer_context);
$this->dependencies[$depend] = " $depend:\n " . str_replace("\n", "\n ", $exported_yaml) . "\n";
}
}
}
}
}
}
// Look for references content_sync has missed.
$walk = function (&$elem, &$content_sync) use (&$walk, $serializer_context) {
if (is_array($elem)) {
if (isset($elem['target_type']) && $elem['target_type'] != 'user' && $elem['target_type'] != 'node' && isset($elem['target_uuid'])) {
if ($entities = $this->entityTypeManager->getStorage($elem['target_type'])->loadByProperties(['uuid' => $elem['target_uuid']])) {
$entity = reset($entities);
if ($elem['target_type'] == 'mm_tree') {
unset($elem['target_type']);
unset($elem['target_uuid']);
$elem['target_id'] = $entity->id();
}
else {
$depend = implode(ContentSyncManager::DELIMITER, [$entity->getEntityTypeId(), $entity->bundle(), $entity->uuid()]);
if (!isset($this->dependencies[$depend])) {
// Prevent the recursive call to this function from exporting the
// dependency again.
$this->dependencies[$depend] = TRUE;
$exported_yaml = trim($this->contentExporter->exportEntity($entity, $serializer_context));
$exported_yaml = $this->addDependencies($exported_yaml, $serializer_context);
$this->dependencies[$depend] = " $depend:\n " . str_replace("\n", "\n ", $exported_yaml) . "\n";
$content_sync[$entity->getEntityTypeId()][] = $depend;
}
}
}
}
else {
foreach ($elem as &$e) {
$walk($e, $content_sync);
}
}
}
};
$walk($exported_entity, $exported_entity['_content_sync']['entity_dependencies']);
unset($exported_entity['_content_sync']['entity_dependencies']['user']);
unset($exported_entity['_content_sync']['entity_dependencies']['mm_tree']);
return Yaml::encode($exported_entity);
}
/**
* Export a field to YAML.
*/
private function exportVar($var, $prefix = '') {
$var = (array) $var;
$dumper = new Dumper(2);
return $dumper->dump($var, 100, strlen($prefix));
// return $prefix . str_replace("\n", "\n$prefix", Yaml::encode($var));
}
private function uid2username($uid) {
if (!isset($this->users[$uid])) {
$loaded = User::load($uid);
if (!$loaded) {
throw new MMImportExportException('Unknown user with uid=@uid.', ['@uid' => $uid]);
}
$this->users[$uid] = $loaded->getAccountName();
}
return $this->users[$uid];
}
private function addGroup($gid, $orig_mmtid = NULL) {
$groups_mmtid = mm_content_groups_mmtid();
$out = [];
foreach (mm_content_get_parents_with_self($gid) as $mmtid) {
if ($mmtid != 1 && $mmtid != $groups_mmtid) {
if (!isset($this->pool[$mmtid])) {
if ($orig_mmtid && in_array($mmtid, $this->subrequest)) {
throw new MMImportExportException('The groups with mmtid=@mmtid1 and mmtid=@mmtid2 have a circular reference which cannot be exported.', ['@mmtid1' => $orig_mmtid, '@mmtid2' => $mmtid]);
}
$temp_item = mm_content_get($mmtid, [Constants::MM_GET_FLAGS]);
$temp_item->perms = mm_content_user_can($mmtid);
$this->subrequest[] = $mmtid;
$this->iterate($temp_item);
array_pop($this->subrequest);
}
$out[] = (int) $mmtid;
}
}
return $out;
}
}
