<?php
/**
 * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
 * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
 * SPDX-License-Identifier: AGPL-3.0-only
 */
namespace OCA\Files_External\Config;

use OC\Files\Cache\Storage;
use OC\Files\Storage\FailedStorage;
use OC\Files\Storage\Wrapper\Availability;
use OC\Files\Storage\Wrapper\KnownMtime;
use OCA\Files_External\Lib\PersonalMount;
use OCA\Files_External\Lib\StorageConfig;
use OCA\Files_External\MountConfig;
use OCA\Files_External\Service\UserGlobalStoragesService;
use OCA\Files_External\Service\UserStoragesService;
use OCP\AppFramework\QueryException;
use OCP\Files\Config\IMountProvider;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\ObjectStore\IObjectStore;
use OCP\Files\Storage\IConstructableStorage;
use OCP\Files\Storage\IStorage;
use OCP\Files\Storage\IStorageFactory;
use OCP\Files\StorageNotAvailableException;
use OCP\IUser;
use OCP\Server;
use Psr\Clock\ClockInterface;
use Psr\Log\LoggerInterface;

/**
 * Make the old files_external config work with the new public mount config api
 */
class ConfigAdapter implements IMountProvider {
	public function __construct(
		private UserStoragesService $userStoragesService,
		private UserGlobalStoragesService $userGlobalStoragesService,
		private ClockInterface $clock,
	) {
	}

	/**
	 * @param class-string $class
	 * @return class-string<IObjectStore>
	 * @throws \InvalidArgumentException
	 * @psalm-taint-escape callable
	 */
	private function validateObjectStoreClassString(string $class): string {
		if (!\is_subclass_of($class, IObjectStore::class)) {
			throw new \InvalidArgumentException('Invalid object store');
		}
		return $class;
	}

	/**
	 * Process storage ready for mounting
	 *
	 * @throws QueryException
	 */
	private function prepareStorageConfig(StorageConfig &$storage, IUser $user): void {
		foreach ($storage->getBackendOptions() as $option => $value) {
			$storage->setBackendOption($option, MountConfig::substitutePlaceholdersInConfig($value, $user->getUID()));
		}

		$objectStore = $storage->getBackendOption('objectstore');
		if ($objectStore) {
			$objectClass = $this->validateObjectStoreClassString($objectStore['class']);
			$storage->setBackendOption('objectstore', new $objectClass($objectStore));
		}

		$storage->getAuthMechanism()->manipulateStorageConfig($storage, $user);
		$storage->getBackend()->manipulateStorageConfig($storage, $user);
	}

	/**
	 * Construct the storage implementation
	 *
	 * @param StorageConfig $storageConfig
	 */
	private function constructStorage(StorageConfig $storageConfig): IStorage {
		$class = $storageConfig->getBackend()->getStorageClass();
		if (!is_a($class, IConstructableStorage::class, true)) {
			Server::get(LoggerInterface::class)->warning('Building a storage not implementing IConstructableStorage is deprecated since 31.0.0', ['class' => $class]);
		}
		$storage = new $class($storageConfig->getBackendOptions());

		// auth mechanism should fire first
		$storage = $storageConfig->getBackend()->wrapStorage($storage);
		$storage = $storageConfig->getAuthMechanism()->wrapStorage($storage);

		return $storage;
	}

	/**
	 * Get all mountpoints applicable for the user
	 *
	 * @return IMountPoint[]
	 */
	public function getMountsForUser(IUser $user, IStorageFactory $loader) {
		$this->userStoragesService->setUser($user);
		$this->userGlobalStoragesService->setUser($user);

		$storageConfigs = $this->userGlobalStoragesService->getAllStoragesForUser();

		$storages = array_map(function (StorageConfig $storageConfig) use ($user) {
			try {
				$this->prepareStorageConfig($storageConfig, $user);
				return $this->constructStorage($storageConfig);
			} catch (\Exception $e) {
				// propagate exception into filesystem
				return new FailedStorage(['exception' => $e]);
			}
		}, $storageConfigs);


		Storage::getGlobalCache()->loadForStorageIds(array_map(function (IStorage $storage) {
			return $storage->getId();
		}, $storages));

		$availableStorages = array_map(function (IStorage $storage, StorageConfig $storageConfig): IStorage {
			try {
				$availability = $storage->getAvailability();
				if (!$availability['available'] && !Availability::shouldRecheck($availability)) {
					$storage = new FailedStorage([
						'exception' => new StorageNotAvailableException('Storage with mount id ' . $storageConfig->getId() . ' is not available')
					]);
				}
			} catch (\Exception $e) {
				// propagate exception into filesystem
				$storage = new FailedStorage(['exception' => $e]);
			}
			return $storage;
		}, $storages, $storageConfigs);

		$mounts = array_map(function (StorageConfig $storageConfig, IStorage $storage) use ($user, $loader) {
			$storage->setOwner($user->getUID());
			if ($storageConfig->getType() === StorageConfig::MOUNT_TYPE_PERSONAL) {
				return new PersonalMount(
					$this->userStoragesService,
					$storageConfig,
					$storageConfig->getId(),
					new KnownMtime([
						'storage' => $storage,
						'clock' => $this->clock,
					]),
					'/' . $user->getUID() . '/files' . $storageConfig->getMountPoint(),
					null,
					$loader,
					$storageConfig->getMountOptions(),
					$storageConfig->getId()
				);
			} else {
				return new SystemMountPoint(
					$storageConfig,
					$storage,
					'/' . $user->getUID() . '/files' . $storageConfig->getMountPoint(),
					null,
					$loader,
					$storageConfig->getMountOptions(),
					$storageConfig->getId()
				);
			}
		}, $storageConfigs, $availableStorages);

		$this->userStoragesService->resetUser();
		$this->userGlobalStoragesService->resetUser();

		return $mounts;
	}
}
t'>remote-run/flow-atmosphere-test</option>
<option value='snapshot/7.5'>snapshot/7.5</option>
<option value='snapshot/7.6'>snapshot/7.6</option>
<option value='tmattsso-patch-1'>tmattsso-patch-1</option>
<option value='update-minor-deps'>update-minor-deps</option>
<option value='vaadin-icons-3.0.1'>vaadin-icons-3.0.1</option>
<option value='wip-date-time-datatype'>wip-date-time-datatype</option>
<option value='wip-java-10-compat'>wip-java-10-compat</option>
</select> <input type='submit' value='switch'/></form></td></tr>
<tr><td class='sub'>Vaadin 6, 7, 8 is a Java framework for modern Java web applications: https://github.com/vaadin/framework</td><td class='sub right'>www-data</td></tr></table>
<table class='tabs'><tr><td>
<a href='/vaadin-framework.git/about/?h=JS-RPC-docs-link-syntax'>about</a><a href='/vaadin-framework.git/?h=JS-RPC-docs-link-syntax'>summary</a><a href='/vaadin-framework.git/refs/?h=JS-RPC-docs-link-syntax&amp;id=ff52c42738a96c3fde01c40d33075376bcae1339'>refs</a><a href='/vaadin-framework.git/log/documentation/gwt/gwt-componentcontainer.asciidoc?h=JS-RPC-docs-link-syntax'>log</a><a class='active' href='/vaadin-framework.git/tree/documentation/gwt/gwt-componentcontainer.asciidoc?h=JS-RPC-docs-link-syntax&amp;id=ff52c42738a96c3fde01c40d33075376bcae1339'>tree</a><a href='/vaadin-framework.git/commit/documentation/gwt/gwt-componentcontainer.asciidoc?h=JS-RPC-docs-link-syntax&amp;id=ff52c42738a96c3fde01c40d33075376bcae1339'>commit</a><a href='/vaadin-framework.git/diff/documentation/gwt/gwt-componentcontainer.asciidoc?h=JS-RPC-docs-link-syntax&amp;id=ff52c42738a96c3fde01c40d33075376bcae1339'>diff</a><a href='/vaadin-framework.git/stats/documentation/gwt/gwt-componentcontainer.asciidoc?h=JS-RPC-docs-link-syntax'>stats</a></td><td class='form'><form class='right' method='get' action='/vaadin-framework.git/log/documentation/gwt/gwt-componentcontainer.asciidoc'>
<input type='hidden' name='h' value='JS-RPC-docs-link-syntax'/><input type='hidden' name='id' value='ff52c42738a96c3fde01c40d33075376bcae1339'/><select name='qt'>
<option value='grep'>log msg</option>
<option value='author'>author</option>
<option value='committer'>committer</option>
<option value='range'>range</option>
</select>
<input class='txt' type='search' size='10' name='q' value=''/>
<input type='submit' value='search'/>
</form>
</td></tr></table>
<div class='path'>path: <a href='/vaadin-framework.git/tree/?h=JS-RPC-docs-link-syntax&amp;id=ff52c42738a96c3fde01c40d33075376bcae1339'>root</a>/<a href='/vaadin-framework.git/tree/documentation?h=JS-RPC-docs-link-syntax&amp;id=ff52c42738a96c3fde01c40d33075376bcae1339'>documentation</a>/<a href='/vaadin-framework.git/tree/documentation/gwt?h=JS-RPC-docs-link-syntax&amp;id=ff52c42738a96c3fde01c40d33075376bcae1339'>gwt</a>/<a href='/vaadin-framework.git/tree/documentation/gwt/gwt-componentcontainer.asciidoc?h=JS-RPC-docs-link-syntax&amp;id=ff52c42738a96c3fde01c40d33075376bcae1339'>gwt-componentcontainer.asciidoc</a></div><div class='content'>blob: 8bdb4e6d7fd4a810654aa311ee67741b6305659b (<a href='/vaadin-framework.git/plain/documentation/gwt/gwt-componentcontainer.asciidoc?h=JS-RPC-docs-link-syntax&amp;id=ff52c42738a96c3fde01c40d33075376bcae1339'>plain</a>)
<table summary='blob content' class='blob'>
<tr><td class='linenumbers'><pre><a id='n1' href='#n1'>1</a>
<a id='n2' href='#n2'>2</a>
<a id='n3' href='#n3'>3</a>
<a id='n4' href='#n4'>4</a>
<a id='n5' href='#n5'>5</a>
<a id='n6' href='#n6'>6</a>
<a id='n7' href='#n7'>7</a>
<a id='n8' href='#n8'>8</a>
<a id='n9' href='#n9'>9</a>
<a id='n10' href='#n10'>10</a>
<a id='n11' href='#n11'>11</a>
<a id='n12' href='#n12'>12</a>
<a id='n13' href='#n13'>13</a>
<a id='n14' href='#n14'>14</a>
<a id='n15' href='#n15'>15</a>
<a id='n16' href='#n16'>16</a>
<a id='n17' href='#n17'>17</a>
<a id='n18' href='#n18'>18</a>
<a id='n19' href='#n19'>19</a>
<a id='n20' href='#n20'>20</a>
</pre></td>
<td class='lines'><pre><code><style>pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */</style><div class="highlight"><pre><span></span>---
title: Component Containers
order: 9
layout: page
---

[[gwt.componentcontainer]]
= Component Containers

Component containers, such as layout components, are a special group of
components that require some consideration. In addition to handling state, they
need to manage communicating the hierarchy of their contained components to the
other side.

The easiest way to implement a component container is extend the
[classname]#AbstractComponentContainer#, which handles the synchronization of
the container server-side components to the client-side.
</pre></div>
</code></pre></td></tr></table>
</div> <!-- class=content -->
<div class='footer'>generated by <a href='https://git.zx2c4.com/cgit/about/'>cgit v1.2.3</a> (<a href='https://git-scm.com/'>git 2.39.1</a>) at 2025-07-31 20:05:33 +0000</div>
</div> <!-- id=cgit -->
</body>
</html>
