aboutsummaryrefslogtreecommitdiffstats
path: root/apps/provisioning_api/lib/Controller/VerificationController.php
blob: 70535c4906c1052b7c68845f7bb911f0a19a8a6d (plain)
1
2
3
4
5
6
7
8
9
10
11pre { 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:
<?php

declare(strict_types=1);

/**
 * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

namespace OCA\Provisioning_API\Controller;

use InvalidArgumentException;
use OC\Security\Crypto;
use OCP\Accounts\IAccountManager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\BruteForceProtection;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IUserManager;
use OCP\IUserSession;
use OCP\Security\VerificationToken\InvalidTokenException;
use OCP\Security\VerificationToken\IVerificationToken;

#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
class VerificationController extends Controller {

	/** @var Crypto */
	private $crypto;

	public function __construct(
		string $appName,
		IRequest $request,
		private IVerificationToken $verificationToken,
		private IUserManager $userManager,
		private IL10N $l10n,
		private IUserSession $userSession,
		private IAccountManager $accountManager,
		Crypto $crypto,
	) {
		parent::__construct($appName, $request);
		$this->crypto = $crypto;
	}

	/**
	 * @NoSubAdminRequired
	 */
	#[NoAdminRequired]
	#[NoCSRFRequired]
	public function showVerifyMail(string $token, string $userId, string $key): TemplateResponse {
		if ($this->userSession->getUser()->getUID() !== $userId) {
			// not a public page, hence getUser() must return an IUser
			throw new InvalidArgumentException('Logged in account is not mail address owner');
		}
		$email = $this->crypto->decrypt($key);

		return new TemplateResponse(
			'core', 'confirmation', [
				'title' => $this->l10n->t('Email confirmation'),
				'message' => $this->l10n->t('To enable the email address %s please click the button below.', [$email]),
				'action' => $this->l10n->t('Confirm'),
			], TemplateResponse::RENDER_AS_GUEST);
	}

	/**
	 * @NoSubAdminRequired
	 */
	#[NoAdminRequired]
	#[BruteForceProtection(action: 'emailVerification')]
	public function verifyMail(string $token, string $userId, string $key): TemplateResponse {
		$throttle = false;
		try {
			if ($this->userSession->getUser()->getUID() !== $userId) {
				throw new InvalidArgumentException('Logged in account is not mail address owner');
			}
			$email = $this->crypto->decrypt($key);
			$ref = \substr(hash('sha256', $email), 0, 8);

			$user = $this->userManager->get($userId);
			$this->verificationToken->check($token, $user, 'verifyMail' . $ref, $email);

			$userAccount = $this->accountManager->getAccount($user);
			$emailProperty = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL)
				->getPropertyByValue($email);

			if ($emailProperty === null) {
				throw new InvalidArgumentException($this->l10n->t('Email was already removed from account and cannot be confirmed anymore.'));
			}
			$emailProperty->setLocallyVerified(IAccountManager::VERIFIED);
			$this->accountManager->updateAccount($userAccount);
			$this->verificationToken->delete($token, $user, 'verifyMail' . $ref);
		} catch (InvalidTokenException $e) {
			if ($e->getCode() === InvalidTokenException::TOKEN_EXPIRED) {
				$error = $this->l10n->t('Could not verify mail because the token is expired.');
			} else {
				$throttle = true;
				$error = $this->l10n->t('Could not verify mail because the token is invalid.');
			}
		} catch (InvalidArgumentException $e) {
			$error = $e->getMessage();
		} catch (\Exception $e) {
			$error = $this->l10n->t('An unexpected error occurred. Please contact your admin.');
		}

		if (isset($error)) {
			$response = new TemplateResponse(
				'core', 'error', [
					'errors' => [['error' => $error]]
				], TemplateResponse::RENDER_AS_GUEST);
			if ($throttle) {
				$response->throttle();
			}
			return $response;
		}

		return new TemplateResponse(
			'core', 'success', [
				'title' => $this->l10n->t('Email confirmation successful'),
				'message' => $this->l10n->t('Email confirmation successful'),
			], TemplateResponse::RENDER_AS_GUEST);
	}
}
N,GACpBjD,KAAKiS,UAAYhP,EAAQkL,UAG1BL,OAAQ,SAAS7C,GAGhB,GAFAjL,KAAKgS,SAAW/G,EAAKiH,SAAW,KAEV,OAAlBlS,KAAKgS,UAA6C,MAAvBhS,KAAKgS,SAASG,MAAuC,KAAvBnS,KAAKgS,SAAS5T,KAgB1E4B,KAAKoK,IAAIgI,YAAY,kCACrBpS,KAAKoK,IAAIiI,WAjB+E,CACxF,IAAIC,EAAWrH,EAAKiH,SAAWjH,EAAKiH,QAAQjI,YAAcgB,EAAKiH,QAAQjI,WAAW5I,OAAS,EAC3FrB,KAAKoK,IAAIgI,YAAY,kCACjBE,GACHtS,KAAKoK,IAAImI,SAAS,WACiD,IAA/DtH,EAAKiH,QAAQjI,WAAWrF,QAAQqD,GAAGY,MAAM0D,iBAC5CvM,KAAKoK,IAAImI,SAAS,eAElBvS,KAAKoK,IAAImI,SAAS,gBAGnBvS,KAAKoK,IAAImI,SAAS,eAEnBvS,KAAKoK,IAAIoI,OACTxS,KAAKyS,iBAMN,OAAOzS,MAER0S,SAAU,SAAShQ,GAClBA,EAAEiQ,iBAEF,IAAIpE,EAAgB,IAAI/F,IAAIN,MAAM0K,cAAc5S,KAAKgS,UACjD1B,EAAOtQ,KACXuO,EAAc1C,GAAG,SAAU,WAC1ByE,EAAKxC,OAAO,CACXoE,QAAS5B,EAAK0B,aAGhBhS,KAAKiS,UAAUpG,GAAG,gBAAiB,SAASyC,GAI3C,IAHA,IAAIrE,EAAa,GACbQ,EAAS6D,EAAWuE,2BAEhBhV,EAAI,EAAGA,EAAI4M,EAAOpJ,OAAQxD,KACiB,IAA9CoM,EAAWrF,QAAQ6F,EAAO5M,GAAG8Q,aAChC1E,EAAW1I,KAAKkJ,EAAO5M,GAAG8Q,YAIxBL,EAAWG,iBACdxE,EAAW1I,KAAK0G,GAAGY,MAAM0D,iBAI1B+D,EAAK0B,SAAS/H,WAAaA,EAE3BqG,EAAKxC,OAAO,CACXoE,QAAS5B,EAAK0B,aAGhBxJ,IAAIN,MAAM4K,IAAIlK,SAASiF,gBAAgBU,EAAe,mBAIxD/F,IAAIC,QAAQ2G,oBAAsBsC,EA7EnC,oBCvBA,IAAAvR,EAAc1C,EAAQ,GAEtB,iBAAA0C,MAAA,EAA4CvC,EAAAC,EAASsC,EAAA,MAOrD,IAAA8C,EAAA,CAAe8P,KAAA,EAEfhN,eAPAA,EAQAhC,gBAAAiB,GAEavH,EAAQ,EAARA,CAAiE0C,EAAA8C,GAE9E9C,EAAA6S,SAAApV,EAAAD,QAAAwC,EAAA6S,0BCjBApV,EAAAD,QAA2BF,EAAQ,EAARA,EAAiE,IAE5F8D,KAAA,CAAc3D,EAAAC,EAAS,2oNAAyoN,oBCYhqND,EAAAD,QAAA,SAAAgG,GAEA,IAAAsP,EAAA,oBAAArR,eAAAqR,SAEA,IAAAA,EACA,UAAAjP,MAAA,oCAIA,IAAAL,GAAA,iBAAAA,EACA,OAAAA,EAGA,IAAAuP,EAAAD,EAAAE,SAAA,KAAAF,EAAAG,KACAC,EAAAH,EAAAD,EAAAK,SAAAC,QAAA,iBA2DA,OA/BA5P,EAAA4P,QAAA,+DAAAC,EAAAC,GAEA,IAWAC,EAXAC,EAAAF,EACAG,OACAL,QAAA,oBAAAjV,EAAAuV,GAAwC,OAAAA,IACxCN,QAAA,oBAAAjV,EAAAuV,GAAwC,OAAAA,IAGxC,0DAAAC,KAAAH,GACAH,GAQAE,EAFA,IAAAC,EAAA/O,QAAA,MAEA+O,EACG,IAAAA,EAAA/O,QAAA,KAEHsO,EAAAS,EAGAN,EAAAM,EAAAJ,QAAA,YAIA,OAAA7S,KAAAC,UAAA+S,GAAA,yBClFA,IAAAvT,EAAc1C,EAAQ,IAEtB,iBAAA0C,MAAA,EAA4CvC,EAAAC,EAASsC,EAAA,MAOrD,IAAA8C,EAAA,CAAe8P,KAAA,EAEfhN,eAPAA,EAQAhC,gBAAAiB,GAEavH,EAAQ,EAARA,CAAiE0C,EAAA8C,GAE9E9C,EAAA6S,SAAApV,EAAAD,QAAAwC,EAAA6S,0BCjBApV,EAAAD,QAA2BF,EAAQ,EAARA,EAAiE,IAE5F8D,KAAA,CAAc3D,EAAAC,EAAS,4mCAA0mC","file":"additionalScripts.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/js/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 2);\n","\"use strict\";\n\n/*\n MIT License http://www.opensource.org/licenses/mit-license.php\n Author Tobias Koppers @sokra\n*/\n// css base code, injected by the css-loader\nmodule.exports = function (useSourceMap) {\n var list = []; // return the list of modules as css string\n\n list.toString = function toString() {\n return this.map(function (item) {\n var content = cssWithMappingToString(item, useSourceMap);\n\n if (item[2]) {\n return '@media ' + item[2] + '{' + content + '}';\n } else {\n return content;\n }\n }).join('');\n }; // import a list of modules into the list\n\n\n list.i = function (modules, mediaQuery) {\n if (typeof modules === 'string') {\n modules = [[null, modules, '']];\n }\n\n var alreadyImportedModules = {};\n\n for (var i = 0; i < this.length; i++) {\n var id = this[i][0];\n\n if (id != null) {\n alreadyImportedModules[id] = true;\n }\n }\n\n for (i = 0; i < modules.length; i++) {\n var item = modules[i]; // skip already imported module\n // this implementation is not 100% perfect for weird media query combinations\n // when a module is imported multiple times with different media queries.\n // I hope this will never occur (Hey this way we have smaller bundles)\n\n if (item[0] == null || !alreadyImportedModules[item[0]]) {\n if (mediaQuery && !item[2]) {\n item[2] = mediaQuery;\n } else if (mediaQuery) {\n item[2] = '(' + item[2] + ') and (' + mediaQuery + ')';\n }\n\n list.push(item);\n }\n }\n };\n\n return list;\n};\n\nfunction cssWithMappingToString(item, useSourceMap) {\n var content = item[1] || '';\n var cssMapping = item[3];\n\n if (!cssMapping) {\n return content;\n }\n\n if (useSourceMap && typeof btoa === 'function') {\n var sourceMapping = toComment(cssMapping);\n var sourceURLs = cssMapping.sources.map(function (source) {\n return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */';\n });\n return [content].concat(sourceURLs).concat([sourceMapping]).join('\\n');\n }\n\n return [content].join('\\n');\n} // Adapted from convert-source-map (MIT)\n\n\nfunction toComment(sourceMap) {\n // eslint-disable-next-line no-undef\n var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));\n var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64;\n return '/*# ' + data + ' */';\n}","/*\n\tMIT License http://www.opensource.org/licenses/mit-license.php\n\tAuthor Tobias Koppers @sokra\n*/\n\nvar stylesInDom = {};\n\nvar\tmemoize = function (fn) {\n\tvar memo;\n\n\treturn function () {\n\t\tif (typeof memo === \"undefined\") memo = fn.apply(this, arguments);\n\t\treturn memo;\n\t};\n};\n\nvar isOldIE = memoize(function () {\n\t// Test for IE <= 9 as proposed by Browserhacks\n\t// @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805\n\t// Tests for existence of standard globals is to allow style-loader\n\t// to operate correctly into non-standard environments\n\t// @see https://github.com/webpack-contrib/style-loader/issues/177\n\treturn window && document && document.all && !window.atob;\n});\n\nvar getTarget = function (target, parent) {\n if (parent){\n return parent.querySelector(target);\n }\n return document.querySelector(target);\n};\n\nvar getElement = (function (fn) {\n\tvar memo = {};\n\n\treturn function(target, parent) {\n // If passing function in options, then use it for resolve \"head\" element.\n // Useful for Shadow Root style i.e\n // {\n // insertInto: function () { return document.querySelector(\"#foo\").shadowRoot }\n // }\n if (typeof target === 'function') {\n return target();\n }\n if (typeof memo[target] === \"undefined\") {\n\t\t\tvar styleTarget = getTarget.call(this, target, parent);\n\t\t\t// Special case to return head of iframe instead of iframe itself\n\t\t\tif (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {\n\t\t\t\ttry {\n\t\t\t\t\t// This will throw an exception if access to iframe is blocked\n\t\t\t\t\t// due to cross-origin restrictions\n\t\t\t\t\tstyleTarget = styleTarget.contentDocument.head;\n\t\t\t\t} catch(e) {\n\t\t\t\t\tstyleTarget = null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tmemo[target] = styleTarget;\n\t\t}\n\t\treturn memo[target]\n\t};\n})();\n\nvar singleton = null;\nvar\tsingletonCounter = 0;\nvar\tstylesInsertedAtTop = [];\n\nvar\tfixUrls = require(\"./urls\");\n\nmodule.exports = function(list, options) {\n\tif (typeof DEBUG !== \"undefined\" && DEBUG) {\n\t\tif (typeof document !== \"object\") throw new Error(\"The style-loader cannot be used in a non-browser environment\");\n\t}\n\n\toptions = options || {};\n\n\toptions.attrs = typeof options.attrs === \"object\" ? options.attrs : {};\n\n\t// Force single-tag solution on IE6-9, which has a hard limit on the # of <style>\n\t// tags it will allow on a page\n\tif (!options.singleton && typeof options.singleton !== \"boolean\") options.singleton = isOldIE();\n\n\t// By default, add <style> tags to the <head> element\n if (!options.insertInto) options.insertInto = \"head\";\n\n\t// By default, add <style> tags to the bottom of the target\n\tif (!options.insertAt) options.insertAt = \"bottom\";\n\n\tvar styles = listToStyles(list, options);\n\n\taddStylesToDom(styles, options);\n\n\treturn function update (newList) {\n\t\tvar mayRemove = [];\n\n\t\tfor (var i = 0; i < styles.length; i++) {\n\t\t\tvar item = styles[i];\n\t\t\tvar domStyle = stylesInDom[item.id];\n\n\t\t\tdomStyle.refs--;\n\t\t\tmayRemove.push(domStyle);\n\t\t}\n\n\t\tif(newList) {\n\t\t\tvar newStyles = listToStyles(newList, options);\n\t\t\taddStylesToDom(newStyles, options);\n\t\t}\n\n\t\tfor (var i = 0; i < mayRemove.length; i++) {\n\t\t\tvar domStyle = mayRemove[i];\n\n\t\t\tif(domStyle.refs === 0) {\n\t\t\t\tfor (var j = 0; j < domStyle.parts.length; j++) domStyle.parts[j]();\n\n\t\t\t\tdelete stylesInDom[domStyle.id];\n\t\t\t}\n\t\t}\n\t};\n};\n\nfunction addStylesToDom (styles, options) {\n\tfor (var i = 0; i < styles.length; i++) {\n\t\tvar item = styles[i];\n\t\tvar domStyle = stylesInDom[item.id];\n\n\t\tif(domStyle) {\n\t\t\tdomStyle.refs++;\n\n\t\t\tfor(var j = 0; j < domStyle.parts.length; j++) {\n\t\t\t\tdomStyle.parts[j](item.parts[j]);\n\t\t\t}\n\n\t\t\tfor(; j < item.parts.length; j++) {\n\t\t\t\tdomStyle.parts.push(addStyle(item.parts[j], options));\n\t\t\t}\n\t\t} else {\n\t\t\tvar parts = [];\n\n\t\t\tfor(var j = 0; j < item.parts.length; j++) {\n\t\t\t\tparts.push(addStyle(item.parts[j], options));\n\t\t\t}\n\n\t\t\tstylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};\n\t\t}\n\t}\n}\n\nfunction listToStyles (list, options) {\n\tvar styles = [];\n\tvar newStyles = {};\n\n\tfor (var i = 0; i < list.length; i++) {\n\t\tvar item = list[i];\n\t\tvar id = options.base ? item[0] + options.base : item[0];\n\t\tvar css = item[1];\n\t\tvar media = item[2];\n\t\tvar sourceMap = item[3];\n\t\tvar part = {css: css, media: media, sourceMap: sourceMap};\n\n\t\tif(!newStyles[id]) styles.push(newStyles[id] = {id: id, parts: [part]});\n\t\telse newStyles[id].parts.push(part);\n\t}\n\n\treturn styles;\n}\n\nfunction insertStyleElement (options, style) {\n\tvar target = getElement(options.insertInto)\n\n\tif (!target) {\n\t\tthrow new Error(\"Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.\");\n\t}\n\n\tvar lastStyleElementInsertedAtTop = stylesInsertedAtTop[stylesInsertedAtTop.length - 1];\n\n\tif (options.insertAt === \"top\") {\n\t\tif (!lastStyleElementInsertedAtTop) {\n\t\t\ttarget.insertBefore(style, target.firstChild);\n\t\t} else if (lastStyleElementInsertedAtTop.nextSibling) {\n\t\t\ttarget.insertBefore(style, lastStyleElementInsertedAtTop.nextSibling);\n\t\t} else {\n\t\t\ttarget.appendChild(style);\n\t\t}\n\t\tstylesInsertedAtTop.push(style);\n\t} else if (options.insertAt === \"bottom\") {\n\t\ttarget.appendChild(style);\n\t} else if (typeof options.insertAt === \"object\" && options.insertAt.before) {\n\t\tvar nextSibling = getElement(options.insertAt.before, target);\n\t\ttarget.insertBefore(style, nextSibling);\n\t} else {\n\t\tthrow new Error(\"[Style Loader]\\n\\n Invalid value for parameter 'insertAt' ('options.insertAt') found.\\n Must be 'top', 'bottom', or Object.\\n (https://github.com/webpack-contrib/style-loader#insertat)\\n\");\n\t}\n}\n\nfunction removeStyleElement (style) {\n\tif (style.parentNode === null) return false;\n\tstyle.parentNode.removeChild(style);\n\n\tvar idx = stylesInsertedAtTop.indexOf(style);\n\tif(idx >= 0) {\n\t\tstylesInsertedAtTop.splice(idx, 1);\n\t}\n}\n\nfunction createStyleElement (options) {\n\tvar style = document.createElement(\"style\");\n\n\tif(options.attrs.type === undefined) {\n\t\toptions.attrs.type = \"text/css\";\n\t}\n\n\tif(options.attrs.nonce === undefined) {\n\t\tvar nonce = getNonce();\n\t\tif (nonce) {\n\t\t\toptions.attrs.nonce = nonce;\n\t\t}\n\t}\n\n\taddAttrs(style, options.attrs);\n\tinsertStyleElement(options, style);\n\n\treturn style;\n}\n\nfunction createLinkElement (options) {\n\tvar link = document.createElement(\"link\");\n\n\tif(options.attrs.type === undefined) {\n\t\toptions.attrs.type = \"text/css\";\n\t}\n\toptions.attrs.rel = \"stylesheet\";\n\n\taddAttrs(link, options.attrs);\n\tinsertStyleElement(options, link);\n\n\treturn link;\n}\n\nfunction addAttrs (el, attrs) {\n\tObject.keys(attrs).forEach(function (key) {\n\t\tel.setAttribute(key, attrs[key]);\n\t});\n}\n\nfunction getNonce() {\n\tif (typeof __webpack_nonce__ === 'undefined') {\n\t\treturn null;\n\t}\n\n\treturn __webpack_nonce__;\n}\n\nfunction addStyle (obj, options) {\n\tvar style, update, remove, result;\n\n\t// If a transform function was defined, run it on the css\n\tif (options.transform && obj.css) {\n\t result = typeof options.transform === 'function'\n\t\t ? options.transform(obj.css) \n\t\t : options.transform.default(obj.css);\n\n\t if (result) {\n\t \t// If transform returns a value, use that instead of the original css.\n\t \t// This allows running runtime transformations on the css.\n\t \tobj.css = result;\n\t } else {\n\t \t// If the transform function returns a falsy value, don't add this css.\n\t \t// This allows conditional loading of css\n\t \treturn function() {\n\t \t\t// noop\n\t \t};\n\t }\n\t}\n\n\tif (options.singleton) {\n\t\tvar styleIndex = singletonCounter++;\n\n\t\tstyle = singleton || (singleton = createStyleElement(options));\n\n\t\tupdate = applyToSingletonTag.bind(null, style, styleIndex, false);\n\t\tremove = applyToSingletonTag.bind(null, style, styleIndex, true);\n\n\t} else if (\n\t\tobj.sourceMap &&\n\t\ttypeof URL === \"function\" &&\n\t\ttypeof URL.createObjectURL === \"function\" &&\n\t\ttypeof URL.revokeObjectURL === \"function\" &&\n\t\ttypeof Blob === \"function\" &&\n\t\ttypeof btoa === \"function\"\n\t) {\n\t\tstyle = createLinkElement(options);\n\t\tupdate = updateLink.bind(null, style, options);\n\t\tremove = function () {\n\t\t\tremoveStyleElement(style);\n\n\t\t\tif(style.href) URL.revokeObjectURL(style.href);\n\t\t};\n\t} else {\n\t\tstyle = createStyleElement(options);\n\t\tupdate = applyToTag.bind(null, style);\n\t\tremove = function () {\n\t\t\tremoveStyleElement(style);\n\t\t};\n\t}\n\n\tupdate(obj);\n\n\treturn function updateStyle (newObj) {\n\t\tif (newObj) {\n\t\t\tif (\n\t\t\t\tnewObj.css === obj.css &&\n\t\t\t\tnewObj.media === obj.media &&\n\t\t\t\tnewObj.sourceMap === obj.sourceMap\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tupdate(obj = newObj);\n\t\t} else {\n\t\t\tremove();\n\t\t}\n\t};\n}\n\nvar replaceText = (function () {\n\tvar textStore = [];\n\n\treturn function (index, replacement) {\n\t\ttextStore[index] = replacement;\n\n\t\treturn textStore.filter(Boolean).join('\\n');\n\t};\n})();\n\nfunction applyToSingletonTag (style, index, remove, obj) {\n\tvar css = remove ? \"\" : obj.css;\n\n\tif (style.styleSheet) {\n\t\tstyle.styleSheet.cssText = replaceText(index, css);\n\t} else {\n\t\tvar cssNode = document.createTextNode(css);\n\t\tvar childNodes = style.childNodes;\n\n\t\tif (childNodes[index]) style.removeChild(childNodes[index]);\n\n\t\tif (childNodes.length) {\n\t\t\tstyle.insertBefore(cssNode, childNodes[index]);\n\t\t} else {\n\t\t\tstyle.appendChild(cssNode);\n\t\t}\n\t}\n}\n\nfunction applyToTag (style, obj) {\n\tvar css = obj.css;\n\tvar media = obj.media;\n\n\tif(media) {\n\t\tstyle.setAttribute(\"media\", media)\n\t}\n\n\tif(style.styleSheet) {\n\t\tstyle.styleSheet.cssText = css;\n\t} else {\n\t\twhile(style.firstChild) {\n\t\t\tstyle.removeChild(style.firstChild);\n\t\t}\n\n\t\tstyle.appendChild(document.createTextNode(css));\n\t}\n}\n\nfunction updateLink (link, options, obj) {\n\tvar css = obj.css;\n\tvar sourceMap = obj.sourceMap;\n\n\t/*\n\t\tIf convertToAbsoluteUrls isn't defined, but sourcemaps are enabled\n\t\tand there is no publicPath defined then lets turn convertToAbsoluteUrls\n\t\ton by default. Otherwise default to the convertToAbsoluteUrls option\n\t\tdirectly\n\t*/\n\tvar autoFixUrls = options.convertToAbsoluteUrls === undefined && sourceMap;\n\n\tif (options.convertToAbsoluteUrls || autoFixUrls) {\n\t\tcss = fixUrls(css);\n\t}\n\n\tif (sourceMap) {\n\t\t// http://stackoverflow.com/a/26603875\n\t\tcss += \"\\n/*# sourceMappingURL=data:application/json;base64,\" + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + \" */\";\n\t}\n\n\tvar blob = new Blob([css], { type: \"text/css\" });\n\n\tvar oldSrc = link.href;\n\n\tlink.href = URL.createObjectURL(blob);\n\n\tif(oldSrc) URL.revokeObjectURL(oldSrc);\n}\n","import './share'\nimport './sharetabview'\nimport './sharebreadcrumbview'\n\nimport './style/sharetabview.scss'\nimport './style/sharebreadcrumb.scss'\n","/*\n * Copyright (c) 2014\n *\n * This file is licensed under the Affero General Public License version 3\n * or later.\n *\n * See the COPYING-README file.\n *\n */\n\n(function() {\n\n\t_.extend(OC.Files.Client, {\n\t\tPROPERTY_SHARE_TYPES:\t'{' + OC.Files.Client.NS_OWNCLOUD + '}share-types',\n\t\tPROPERTY_OWNER_ID:\t'{' + OC.Files.Client.NS_OWNCLOUD + '}owner-id',\n\t\tPROPERTY_OWNER_DISPLAY_NAME:\t'{' + OC.Files.Client.NS_OWNCLOUD + '}owner-display-name'\n\t});\n\n\tif (!OCA.Sharing) {\n\t\tOCA.Sharing = {};\n\t}\n\t/**\n\t * @namespace\n\t */\n\tOCA.Sharing.Util = {\n\t\t/**\n\t\t * Initialize the sharing plugin.\n\t\t *\n\t\t * Registers the \"Share\" file action and adds additional\n\t\t * DOM attributes for the sharing file info.\n\t\t *\n\t\t * @param {OCA.Files.FileList} fileList file list to be extended\n\t\t */\n\t\tattach: function(fileList) {\n\t\t\t// core sharing is disabled/not loaded\n\t\t\tif (!OC.Share) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (fileList.id === 'trashbin' || fileList.id === 'files.public') {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvar fileActions = fileList.fileActions;\n\t\t\tvar oldCreateRow = fileList._createRow;\n\t\t\tfileList._createRow = function(fileData) {\n\n\t\t\t\tvar tr = oldCreateRow.apply(this, arguments);\n\t\t\t\tvar sharePermissions = OCA.Sharing.Util.getSharePermissions(fileData);\n\t\t\t\t\n\t\t\t\tif (fileData.permissions === 0) {\n\t\t\t\t\t// no permission, disabling sidebar\n\t\t\t\t\tdelete fileActions.actions.all.Comment;\n\t\t\t\t\tdelete fileActions.actions.all.Details;\n\t\t\t\t\tdelete fileActions.actions.all.Goto;\n\t\t\t\t}\n\t\t\t\ttr.attr('data-share-permissions', sharePermissions);\n\t\t\t\tif (fileData.shareOwner) {\n\t\t\t\t\ttr.attr('data-share-owner', fileData.shareOwner);\n\t\t\t\t\ttr.attr('data-share-owner-id', fileData.shareOwnerId);\n\t\t\t\t\t// user should always be able to rename a mount point\n\t\t\t\t\tif (fileData.mountType === 'shared-root') {\n\t\t\t\t\t\ttr.attr('data-permissions', fileData.permissions | OC.PERMISSION_UPDATE);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (fileData.recipientData && !_.isEmpty(fileData.recipientData)) {\n\t\t\t\t\ttr.attr('data-share-recipient-data', JSON.stringify(fileData.recipientData));\n\t\t\t\t}\n\t\t\t\tif (fileData.shareTypes) {\n\t\t\t\t\ttr.attr('data-share-types', fileData.shareTypes.join(','));\n\t\t\t\t}\n\t\t\t\treturn tr;\n\t\t\t};\n\n\t\t\tvar oldElementToFile = fileList.elementToFile;\n\t\t\tfileList.elementToFile = function($el) {\n\t\t\t\tvar fileInfo = oldElementToFile.apply(this, arguments);\n\t\t\t\tfileInfo.sharePermissions = $el.attr('data-share-permissions') || undefined;\n\t\t\t\tfileInfo.shareOwner = $el.attr('data-share-owner') || undefined;\n\t\t\t\tfileInfo.shareOwnerId = $el.attr('data-share-owner-id') || undefined;\n\n\t\t\t\tif( $el.attr('data-share-types')){\n\t\t\t\t\tfileInfo.shareTypes = $el.attr('data-share-types').split(',');\n\t\t\t\t}\n\n\t\t\t\tif( $el.attr('data-expiration')){\n\t\t\t\t\tvar expirationTimestamp = parseInt($el.attr('data-expiration'));\n\t\t\t\t\tfileInfo.shares = [];\n\t\t\t\t\tfileInfo.shares.push({expiration: expirationTimestamp});\n\t\t\t\t}\n\n\t\t\t\treturn fileInfo;\n\t\t\t};\n\n\t\t\tvar oldGetWebdavProperties = fileList._getWebdavProperties;\n\t\t\tfileList._getWebdavProperties = function() {\n\t\t\t\tvar props = oldGetWebdavProperties.apply(this, arguments);\n\t\t\t\tprops.push(OC.Files.Client.PROPERTY_OWNER_ID);\n\t\t\t\tprops.push(OC.Files.Client.PROPERTY_OWNER_DISPLAY_NAME);\n\t\t\t\tprops.push(OC.Files.Client.PROPERTY_SHARE_TYPES);\n\t\t\t\treturn props;\n\t\t\t};\n\n\t\t\tfileList.filesClient.addFileInfoParser(function(response) {\n\t\t\t\tvar data = {};\n\t\t\t\tvar props = response.propStat[0].properties;\n\t\t\t\tvar permissionsProp = props[OC.Files.Client.PROPERTY_PERMISSIONS];\n\n\t\t\t\tif (permissionsProp && permissionsProp.indexOf('S') >= 0) {\n\t\t\t\t\tdata.shareOwner = props[OC.Files.Client.PROPERTY_OWNER_DISPLAY_NAME];\n\t\t\t\t\tdata.shareOwnerId = props[OC.Files.Client.PROPERTY_OWNER_ID];\n\t\t\t\t}\n\n\t\t\t\tvar shareTypesProp = props[OC.Files.Client.PROPERTY_SHARE_TYPES];\n\t\t\t\tif (shareTypesProp) {\n\t\t\t\t\tdata.shareTypes = _.chain(shareTypesProp).filter(function(xmlvalue) {\n\t\t\t\t\t\treturn (xmlvalue.namespaceURI === OC.Files.Client.NS_OWNCLOUD && xmlvalue.nodeName.split(':')[1] === 'share-type');\n\t\t\t\t\t}).map(function(xmlvalue) {\n\t\t\t\t\t\treturn parseInt(xmlvalue.textContent || xmlvalue.text, 10);\n\t\t\t\t\t}).value();\n\t\t\t\t}\n\n\t\t\t\treturn data;\n\t\t\t});\n\n\t\t\t// use delegate to catch the case with multiple file lists\n\t\t\tfileList.$el.on('fileActionsReady', function(ev){\n\t\t\t\tvar $files = ev.$files;\n\n\t\t\t\t_.each($files, function(file) {\n\t\t\t\t\tvar $tr = $(file);\n\t\t\t\t\tvar shareTypes = $tr.attr('data-share-types') || '';\n\t\t\t\t\tvar shareOwner = $tr.attr('data-share-owner');\n\t\t\t\t\tif (shareTypes || shareOwner) {\n\t\t\t\t\t\tvar hasLink = false;\n\t\t\t\t\t\tvar hasShares = false;\n\t\t\t\t\t\t_.each(shareTypes.split(',') || [], function(shareType) {\n\t\t\t\t\t\t\tshareType = parseInt(shareType, 10);\n\t\t\t\t\t\t\tif (shareType === OC.Share.SHARE_TYPE_LINK) {\n\t\t\t\t\t\t\t\thasLink = true;\n\t\t\t\t\t\t\t} else if (shareType === OC.Share.SHARE_TYPE_EMAIL) {\n\t\t\t\t\t\t\t\thasLink = true;\n\t\t\t\t\t\t\t} else if (shareType === OC.Share.SHARE_TYPE_USER) {\n\t\t\t\t\t\t\t\thasShares = true;\n\t\t\t\t\t\t\t} else if (shareType === OC.Share.SHARE_TYPE_GROUP) {\n\t\t\t\t\t\t\t\thasShares = true;\n\t\t\t\t\t\t\t} else if (shareType === OC.Share.SHARE_TYPE_REMOTE) {\n\t\t\t\t\t\t\t\thasShares = true;\n\t\t\t\t\t\t\t} else if (shareType === OC.Share.SHARE_TYPE_CIRCLE) {\n\t\t\t\t\t\t\t\thasShares = true;\n\t\t\t\t\t\t\t} else if (shareType === OC.Share.SHARE_TYPE_ROOM) {\n\t\t\t\t\t\t\t\thasShares = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t\tOCA.Sharing.Util._updateFileActionIcon($tr, hasShares, hasLink);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\n\n\t\t\tfileList.$el.on('changeDirectory', function() {\n\t\t\t\tOCA.Sharing.sharesLoaded = false;\n\t\t\t});\n\n\t\t\tfileActions.registerAction({\n\t\t\t\tname: 'Share',\n\t\t\t\tdisplayName: '',\n\t\t\t\taltText: t('core', 'Share'),\n\t\t\t\tmime: 'all',\n\t\t\t\tpermissions: OC.PERMISSION_ALL,\n\t\t\t\ticonClass: 'icon-shared',\n\t\t\t\ttype: OCA.Files.FileActions.TYPE_INLINE,\n\t\t\t\tactionHandler: function(fileName, context) {\n\t\t\t\t\t// do not open sidebar if permission is set and equal to 0\n\t\t\t\t\tvar permissions = parseInt(context.$file.data('share-permissions'), 10);\n\t\t\t\t\tif (isNaN(permissions) || permissions > 0) {\n\t\t\t\t\t\tfileList.showDetailsView(fileName, 'shareTabView');\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\trender: function(actionSpec, isDefault, context) {\n\t\t\t\t\tvar permissions = parseInt(context.$file.data('permissions'), 10);\n\t\t\t\t\t// if no share permissions but share owner exists, still show the link\n\t\t\t\t\tif ((permissions & OC.PERMISSION_SHARE) !== 0 || context.$file.attr('data-share-owner')) {\n\t\t\t\t\t\treturn fileActions._defaultRenderAction.call(fileActions, actionSpec, isDefault, context);\n\t\t\t\t\t}\n\t\t\t\t\t// don't render anything\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tvar shareTab = new OCA.Sharing.ShareTabView('shareTabView', {order: -20});\n\t\t\t// detect changes and change the matching list entry\n\t\t\tshareTab.on('sharesChanged', function(shareModel) {\n\t\t\t\tvar fileInfoModel = shareModel.fileInfoModel;\n\t\t\t\tvar $tr = fileList.findFileEl(fileInfoModel.get('name'));\n\n\t\t\t\t// We count email shares as link share\n\t\t\t\tvar hasLinkShares = shareModel.hasLinkShares();\n\t\t\t\tshareModel.get('shares').forEach(function (share) {\n\t\t\t\t\tif (share.share_type === OC.Share.SHARE_TYPE_EMAIL) {\n\t\t\t\t\t\thasLinkShares = true;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tOCA.Sharing.Util._updateFileListDataAttributes(fileList, $tr, shareModel);\n\t\t\t\tif (!OCA.Sharing.Util._updateFileActionIcon($tr, shareModel.hasUserShares(), hasLinkShares)) {\n\t\t\t\t\t// remove icon, if applicable\n\t\t\t\t\tOC.Share.markFileAsShared($tr, false, false);\n\t\t\t\t}\n\n\t\t\t\t// FIXME: this is too convoluted. We need to get rid of the above updates\n\t\t\t\t// and only ever update the model and let the events take care of rerendering\n\t\t\t\tfileInfoModel.set({\n\t\t\t\t\tshareTypes: shareModel.getShareTypes(),\n\t\t\t\t\t// in case markFileAsShared decided to change the icon,\n\t\t\t\t\t// we need to modify the model\n\t\t\t\t\t// (FIXME: yes, this is hacky)\n\t\t\t\t\ticon: $tr.attr('data-icon')\n\t\t\t\t});\n\t\t\t});\n\t\t\tfileList.registerTabView(shareTab);\n\n\t\t\tvar breadCrumbSharingDetailView = new OCA.Sharing.ShareBreadCrumbView({shareTab: shareTab});\n\t\t\tfileList.registerBreadCrumbDetailView(breadCrumbSharingDetailView);\n\t\t},\n\n\t\t/**\n\t\t * Update file list data attributes\n\t\t */\n\t\t_updateFileListDataAttributes: function(fileList, $tr, shareModel) {\n\t\t\t// files app current cannot show recipients on load, so we don't update the\n\t\t\t// icon when changed for consistency\n\t\t\tif (fileList.id === 'files') {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvar recipients = _.pluck(shareModel.get('shares'), 'share_with_displayname');\n\t\t\t// note: we only update the data attribute because updateIcon()\n\t\t\tif (recipients.length) {\n\t\t\t\tvar recipientData = _.mapObject(shareModel.get('shares'), function (share) {\n\t\t\t\t\treturn {shareWith: share.share_with, shareWithDisplayName: share.share_with_displayname};\n\t\t\t\t});\n\t\t\t\t$tr.attr('data-share-recipient-data', JSON.stringify(recipientData));\n\t\t\t}\n\t\t\telse {\n\t\t\t\t$tr.removeAttr('data-share-recipient-data');\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Update the file action share icon for the given file\n\t\t *\n\t\t * @param $tr file element of the file to update\n\t\t * @param {boolean} hasUserShares true if a user share exists\n\t\t * @param {boolean} hasLinkShares true if a link share exists\n\t\t *\n\t\t * @return {boolean} true if the icon was set, false otherwise\n\t\t */\n\t\t_updateFileActionIcon: function($tr, hasUserShares, hasLinkShares) {\n\t\t\t// if the statuses are loaded already, use them for the icon\n\t\t\t// (needed when scrolling to the next page)\n\t\t\tif (hasUserShares || hasLinkShares || $tr.attr('data-share-recipient-data') || $tr.attr('data-share-owner')) {\n\t\t\t\tOC.Share.markFileAsShared($tr, true, hasLinkShares);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t},\n\n\t\t/**\n\t\t * @param {Array} fileData\n\t\t * @returns {String}\n\t\t */\n\t\tgetSharePermissions: function(fileData) {\n\t\t\treturn fileData.sharePermissions;\n\t\t}\n\t};\n})();\n\nOC.Plugins.register('OCA.Files.FileList', OCA.Sharing.Util);\n","/*\n * Copyright (c) 2015\n *\n * This file is licensed under the Affero General Public License version 3\n * or later.\n *\n * See the COPYING-README file.\n *\n */\n\n/* @global Handlebars */\n\n(function() {\n\tvar TEMPLATE =\n\t\t'<div>' +\n\t\t'<div class=\"dialogContainer\"></div>' +\n\t\t'</div>';\n\n\t/**\n\t * @memberof OCA.Sharing\n\t */\n\tvar ShareTabView = OCA.Files.DetailTabView.extend(\n\t\t/** @lends OCA.Sharing.ShareTabView.prototype */ {\n\t\tid: 'shareTabView',\n\t\tclassName: 'tab shareTabView',\n\n\t\tinitialize: function(name, options) {\n\t\t\tOCA.Files.DetailTabView.prototype.initialize.call(this, name, options);\n\t\t\tOC.Plugins.attach('OCA.Sharing.ShareTabView', this);\n\t\t},\n\n\t\ttemplate: function(params) {\n\t\t\treturn \tTEMPLATE;\n\t\t},\n\n\t\tgetLabel: function() {\n\t\t\treturn t('files_sharing', 'Sharing');\n\t\t},\n\n\t\tgetIcon: function() {\n\t\t\treturn 'icon-shared';\n\t\t},\n\n\t\t/**\n\t\t * Renders this details view\n\t\t */\n\t\trender: function() {\n\t\t\tvar self = this;\n\t\t\tif (this._dialog) {\n\t\t\t\t// remove/destroy older instance\n\t\t\t\tthis._dialog.model.off();\n\t\t\t\tthis._dialog.remove();\n\t\t\t\tthis._dialog = null;\n\t\t\t}\n\n\t\t\tif (this.model) {\n\t\t\t\tthis.$el.html(this.template());\n\n\t\t\t\tif (_.isUndefined(this.model.get('sharePermissions'))) {\n\t\t\t\t\tthis.model.set('sharePermissions', OCA.Sharing.Util.getSharePermissions(this.model.attributes));\n\t\t\t\t}\n\n\t\t\t\t// TODO: the model should read these directly off the passed fileInfoModel\n\t\t\t\tvar attributes = {\n\t\t\t\t\titemType: this.model.isDirectory() ? 'folder' : 'file',\n\t\t\t\t \titemSource: this.model.get('id'),\n\t\t\t\t\tpossiblePermissions: this.model.get('sharePermissions')\n\t\t\t\t};\n\t\t\t\tvar configModel = new OC.Share.ShareConfigModel();\n\t\t\t\tvar shareModel = new OC.Share.ShareItemModel(attributes, {\n\t\t\t\t\tconfigModel: configModel,\n\t\t\t\t\tfileInfoModel: this.model\n\t\t\t\t});\n\t\t\t\tthis._dialog = new OC.Share.ShareDialogView({\n\t\t\t\t\tconfigModel: configModel,\n\t\t\t\t\tmodel: shareModel\n\t\t\t\t});\n\t\t\t\tthis.$el.find('.dialogContainer').append(this._dialog.$el);\n\t\t\t\tthis._dialog.render();\n\t\t\t\tthis._dialog.model.fetch();\n\t\t\t\tthis._dialog.model.on('change', function() {\n\t\t\t\t\tself.trigger('sharesChanged', shareModel);\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthis.$el.empty();\n\t\t\t\t// TODO: render placeholder text?\n\t\t\t}\n\t\t\tthis.trigger('rendered');\n\t\t}\n\t});\n\n\tOCA.Sharing.ShareTabView = ShareTabView;\n})();\n\n","/* global Handlebars, OC */\n\n/**\n * @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at>\n *\n * @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at>\n *\n * @license GNU AGPL version 3 or any later version\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\n(function() {\n\t'use strict';\n\n\tvar BreadCrumbView = OC.Backbone.View.extend({\n\t\ttagName: 'span',\n\t\tevents: {\n\t\t\tclick: '_onClick'\n\t\t},\n\t\t_dirInfo: undefined,\n\n\t\t/** @type OCA.Sharing.ShareTabView */\n\t\t_shareTab: undefined,\n\n\t\tinitialize: function(options) {\n\t\t\tthis._shareTab = options.shareTab;\n\t\t},\n\n\t\trender: function(data) {\n\t\t\tthis._dirInfo = data.dirInfo || null;\n\n\t\t\tif (this._dirInfo !== null && (this._dirInfo.path !== '/' || this._dirInfo.name !== '')) {\n\t\t\t\tvar isShared = data.dirInfo && data.dirInfo.shareTypes && data.dirInfo.shareTypes.length > 0;\n\t\t\t\tthis.$el.removeClass('shared icon-public icon-shared');\n\t\t\t\tif (isShared) {\n\t\t\t\t\tthis.$el.addClass('shared');\n\t\t\t\t\tif (data.dirInfo.shareTypes.indexOf(OC.Share.SHARE_TYPE_LINK) !== -1) {\n\t\t\t\t\t\tthis.$el.addClass('icon-public');\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.$el.addClass('icon-shared');\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.$el.addClass('icon-shared');\n\t\t\t\t}\n\t\t\t\tthis.$el.show();\n\t\t\t\tthis.delegateEvents();\n\t\t\t} else {\n\t\t\t\tthis.$el.removeClass('shared icon-public icon-shared');\n\t\t\t\tthis.$el.hide();\n\t\t\t}\n\n\t\t\treturn this;\n\t\t},\n\t\t_onClick: function(e) {\n\t\t\te.preventDefault();\n\n\t\t\tvar fileInfoModel = new OCA.Files.FileInfoModel(this._dirInfo);\n\t\t\tvar self = this;\n\t\t\tfileInfoModel.on('change', function() {\n\t\t\t\tself.render({\n\t\t\t\t\tdirInfo: self._dirInfo\n\t\t\t\t});\n\t\t\t});\n\t\t\tthis._shareTab.on('sharesChanged', function(shareModel) {\n\t\t\t\tvar shareTypes = [];\n\t\t\t\tvar shares = shareModel.getSharesWithCurrentItem();\n\n\t\t\t\tfor(var i = 0; i < shares.length; i++) {\n\t\t\t\t\tif (shareTypes.indexOf(shares[i].share_type) === -1) {\n\t\t\t\t\t\tshareTypes.push(shares[i].share_type);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (shareModel.hasLinkShares()) {\n\t\t\t\t\tshareTypes.push(OC.Share.SHARE_TYPE_LINK);\n\t\t\t\t}\n\n\t\t\t\t// Since the dirInfo isn't updated we need to do this dark hackery\n\t\t\t\tself._dirInfo.shareTypes = shareTypes;\n\n\t\t\t\tself.render({\n\t\t\t\t\tdirInfo: self._dirInfo\n\t\t\t\t});\n\t\t\t});\n\t\t\tOCA.Files.App.fileList.showDetailsView(fileInfoModel, 'shareTabView');\n\t\t}\n\t});\n\n\tOCA.Sharing.ShareBreadCrumbView = BreadCrumbView;\n})();\n","\nvar content = require(\"!!../../../../node_modules/css-loader/dist/cjs.js!../../../../node_modules/sass-loader/lib/loader.js!./sharetabview.scss\");\n\nif(typeof content === 'string') content = [[module.id, content, '']];\n\nvar transform;\nvar insertInto;\n\n\n\nvar options = {\"hmr\":true}\n\noptions.transform = transform\noptions.insertInto = undefined;\n\nvar update = require(\"!../../../../node_modules/style-loader/lib/addStyles.js\")(content, options);\n\nif(content.locals) module.exports = content.locals;\n\nif(module.hot) {\n\tmodule.hot.accept(\"!!../../../../node_modules/css-loader/dist/cjs.js!../../../../node_modules/sass-loader/lib/loader.js!./sharetabview.scss\", function() {\n\t\tvar newContent = require(\"!!../../../../node_modules/css-loader/dist/cjs.js!../../../../node_modules/sass-loader/lib/loader.js!./sharetabview.scss\");\n\n\t\tif(typeof newContent === 'string') newContent = [[module.id, newContent, '']];\n\n\t\tvar locals = (function(a, b) {\n\t\t\tvar key, idx = 0;\n\n\t\t\tfor(key in a) {\n\t\t\t\tif(!b || a[key] !== b[key]) return false;\n\t\t\t\tidx++;\n\t\t\t}\n\n\t\t\tfor(key in b) idx--;\n\n\t\t\treturn idx === 0;\n\t\t}(content.locals, newContent.locals));\n\n\t\tif(!locals) throw new Error('Aborting CSS HMR due to changed css-modules locals.');\n\n\t\tupdate(newContent);\n\t});\n\n\tmodule.hot.dispose(function() { update(); });\n}","exports = module.exports = require(\"../../../../node_modules/css-loader/dist/runtime/api.js\")(false);\n// Module\nexports.push([module.id, \".app-files .shareTabView {\\n min-height: 100px; }\\n\\n.share-autocomplete-item {\\n display: flex; }\\n .share-autocomplete-item.merged {\\n margin-left: 32px; }\\n .share-autocomplete-item .autocomplete-item-text {\\n margin-left: 10px;\\n margin-right: 10px;\\n white-space: nowrap;\\n text-overflow: ellipsis;\\n overflow: hidden;\\n line-height: 32px;\\n vertical-align: middle;\\n flex-grow: 1; }\\n .share-autocomplete-item .autocomplete-item-text .ui-state-highlight {\\n border: none;\\n margin: 0; }\\n .share-autocomplete-item.with-description .autocomplete-item-text {\\n line-height: 100%; }\\n .share-autocomplete-item .autocomplete-item-details {\\n display: block;\\n line-height: 130%;\\n font-size: 90%;\\n opacity: 0.7; }\\n .share-autocomplete-item .icon {\\n opacity: .7;\\n margin-right: 7px; }\\n .share-autocomplete-item .icon.search-globally {\\n width: 32px;\\n height: 32px;\\n margin-right: 0; }\\n\\n.shareTabView .oneline {\\n white-space: nowrap;\\n position: relative; }\\n\\n.shareTabView .shareWithLoading {\\n padding-left: 10px;\\n right: 35px;\\n top: 3px; }\\n\\n.shareTabView .shareWithConfirm {\\n position: absolute;\\n right: 2px;\\n top: 6px;\\n padding: 14px;\\n opacity: 0.5; }\\n\\n.shareTabView .shareWithField:focus ~ .shareWithConfirm {\\n opacity: 1; }\\n\\n.shareTabView .linkMore {\\n position: absolute;\\n right: -7px;\\n top: -4px;\\n padding: 14px; }\\n\\n.shareTabView .popovermenu {\\n /* Border above last entry '+ Add another share' to separate it from current link settings */ }\\n .shareTabView .popovermenu .linkPassMenu .share-pass-submit {\\n width: auto !important; }\\n .shareTabView .popovermenu .linkPassMenu .icon-loading-small {\\n background-color: var(--color-main-background);\\n position: absolute;\\n right: 8px;\\n margin: 3px;\\n padding: 10px;\\n width: 32px;\\n height: 32px;\\n z-index: 10; }\\n .shareTabView .popovermenu .datepicker {\\n margin-left: 35px; }\\n .shareTabView .popovermenu .share-add input.share-note-delete {\\n border: none;\\n background-color: transparent;\\n width: 44px !important;\\n padding: 0;\\n flex: 0 0 44px;\\n margin-left: auto; }\\n .shareTabView .popovermenu .share-add input.share-note-delete.hidden {\\n display: none; }\\n .shareTabView .popovermenu .share-note-form span.icon-note {\\n position: relative; }\\n .shareTabView .popovermenu .share-note-form textarea.share-note {\\n margin: 0;\\n width: 200px;\\n min-height: 70px;\\n resize: none; }\\n .shareTabView .popovermenu .share-note-form textarea.share-note + input.share-note-submit {\\n position: absolute;\\n width: 44px !important;\\n height: 44px;\\n bottom: 0px;\\n right: 10px;\\n margin: 0;\\n background-color: transparent;\\n border: none;\\n opacity: .7; }\\n .shareTabView .popovermenu .share-note-form textarea.share-note + input.share-note-submit:hover, .shareTabView .popovermenu .share-note-form textarea.share-note + input.share-note-submit:focus, .shareTabView .popovermenu .share-note-form textarea.share-note + input.share-note-submit:active {\\n opacity: 1; }\\n .shareTabView .popovermenu .share-note-form.share-note-link {\\n margin-bottom: 10px; }\\n .shareTabView .popovermenu .new-share {\\n border-top: 1px solid var(--color-border); }\\n\\n.shareTabView .linkPass .icon-loading-small {\\n margin-right: 0px; }\\n\\n.shareTabView .icon {\\n background-size: 16px 16px; }\\n\\n.shareTabView .shareWithList .icon-loading-small:not(.hidden) + span,\\n.shareTabView .linkShareView .icon-loading-small:not(.hidden) + input + label:before {\\n /* Hide if loader is visible */\\n display: none !important; }\\n\\n.shareTabView input[type='checkbox'] {\\n margin: 0 3px 0 8px;\\n vertical-align: middle; }\\n\\n.shareTabView input[type='text'].shareWithField, .shareTabView input[type='text'].emailField {\\n width: 100%;\\n box-sizing: border-box;\\n padding-right: 32px;\\n text-overflow: ellipsis; }\\n\\n.shareTabView input[type='text'].linkText .shareTabView input[type='password'].linkPassText, .shareTabView input[type='password'].passwordField {\\n width: 180px !important; }\\n\\n.shareTabView form {\\n font-size: 100%;\\n margin-left: 0;\\n margin-right: 0; }\\n\\n.shareTabView .share-note {\\n border-radius: var(--border-radius);\\n margin-bottom: 10px;\\n margin-left: 37px; }\\n\\n.shareWithList {\\n list-style-type: none;\\n display: flex;\\n flex-direction: column; }\\n .shareWithList > li {\\n height: 44px;\\n white-space: normal;\\n display: inline-flex;\\n align-items: center;\\n position: relative; }\\n .shareWithList > li .avatar {\\n width: 32px;\\n height: 32px;\\n background-color: var(--color-primary); }\\n .shareWithList .unshare img {\\n vertical-align: text-bottom;\\n /* properly align icons */ }\\n .shareWithList .sharingOptionsGroup {\\n margin-left: auto;\\n display: flex;\\n align-items: center;\\n white-space: nowrap; }\\n .shareWithList .sharingOptionsGroup > .icon:not(.hidden),\\n .shareWithList .sharingOptionsGroup .share-menu > .icon:not(.hidden) {\\n padding: 14px;\\n height: 44px;\\n width: 44px;\\n opacity: .5;\\n display: block;\\n cursor: pointer; }\\n .shareWithList .sharingOptionsGroup > .icon:not(.hidden):hover, .shareWithList .sharingOptionsGroup > .icon:not(.hidden):focus, .shareWithList .sharingOptionsGroup > .icon:not(.hidden):active,\\n .shareWithList .sharingOptionsGroup .share-menu > .icon:not(.hidden):hover,\\n .shareWithList .sharingOptionsGroup .share-menu > .icon:not(.hidden):focus,\\n .shareWithList .sharingOptionsGroup .share-menu > .icon:not(.hidden):active {\\n opacity: .7; }\\n .shareWithList .sharingOptionsGroup > .share-menu {\\n position: relative;\\n display: block; }\\n .shareWithList .username {\\n padding: 0 8px;\\n overflow: hidden;\\n white-space: nowrap;\\n text-overflow: ellipsis; }\\n\\n.ui-autocomplete {\\n /* limit dropdown height to 6 1/2 entries */\\n max-height: calc(36px * 6.5);\\n overflow-y: auto;\\n overflow-x: hidden;\\n z-index: 1550 !important; }\\n\\n.notCreatable {\\n padding-left: 12px;\\n padding-top: 12px;\\n color: var(--color-text-lighter); }\\n\\n.contactsmenu-popover {\\n left: -6px;\\n right: auto;\\n padding: 3px 6px;\\n top: 100%;\\n margin-top: 0; }\\n .contactsmenu-popover li.hidden {\\n display: none !important; }\\n .contactsmenu-popover:after {\\n left: 8px;\\n right: auto; }\\n\\n.reshare,\\n#link label,\\n#expiration label {\\n display: inline-flex;\\n align-items: center; }\\n .reshare .avatar,\\n #link label .avatar,\\n #expiration label .avatar {\\n margin-right: 5px; }\\n\\n.resharerInfoView.subView {\\n position: relative; }\\n\", \"\"]);\n\n","\n/**\n * When source maps are enabled, `style-loader` uses a link element with a data-uri to\n * embed the css on the page. This breaks all relative urls because now they are relative to a\n * bundle instead of the current page.\n *\n * One solution is to only use full urls, but that may be impossible.\n *\n * Instead, this function \"fixes\" the relative urls to be absolute according to the current page location.\n *\n * A rudimentary test suite is located at `test/fixUrls.js` and can be run via the `npm test` command.\n *\n */\n\nmodule.exports = function (css) {\n // get current location\n var location = typeof window !== \"undefined\" && window.location;\n\n if (!location) {\n throw new Error(\"fixUrls requires window.location\");\n }\n\n\t// blank or null?\n\tif (!css || typeof css !== \"string\") {\n\t return css;\n }\n\n var baseUrl = location.protocol + \"//\" + location.host;\n var currentDir = baseUrl + location.pathname.replace(/\\/[^\\/]*$/, \"/\");\n\n\t// convert each url(...)\n\t/*\n\tThis regular expression is just a way to recursively match brackets within\n\ta string.\n\n\t /url\\s*\\( = Match on the word \"url\" with any whitespace after it and then a parens\n\t ( = Start a capturing group\n\t (?: = Start a non-capturing group\n\t [^)(] = Match anything that isn't a parentheses\n\t | = OR\n\t \\( = Match a start parentheses\n\t (?: = Start another non-capturing groups\n\t [^)(]+ = Match anything that isn't a parentheses\n\t | = OR\n\t \\( = Match a start parentheses\n\t [^)(]* = Match anything that isn't a parentheses\n\t \\) = Match a end parentheses\n\t ) = End Group\n *\\) = Match anything and then a close parens\n ) = Close non-capturing group\n * = Match anything\n ) = Close capturing group\n\t \\) = Match a close parens\n\n\t /gi = Get all matches, not the first. Be case insensitive.\n\t */\n\tvar fixedCss = css.replace(/url\\s*\\(((?:[^)(]|\\((?:[^)(]+|\\([^)(]*\\))*\\))*)\\)/gi, function(fullMatch, origUrl) {\n\t\t// strip quotes (if they exist)\n\t\tvar unquotedOrigUrl = origUrl\n\t\t\t.trim()\n\t\t\t.replace(/^\"(.*)\"$/, function(o, $1){ return $1; })\n\t\t\t.replace(/^'(.*)'$/, function(o, $1){ return $1; });\n\n\t\t// already a full url? no change\n\t\tif (/^(#|data:|http:\\/\\/|https:\\/\\/|file:\\/\\/\\/|\\s*$)/i.test(unquotedOrigUrl)) {\n\t\t return fullMatch;\n\t\t}\n\n\t\t// convert the url to a full url\n\t\tvar newUrl;\n\n\t\tif (unquotedOrigUrl.indexOf(\"//\") === 0) {\n\t\t \t//TODO: should we add protocol?\n\t\t\tnewUrl = unquotedOrigUrl;\n\t\t} else if (unquotedOrigUrl.indexOf(\"/\") === 0) {\n\t\t\t// path should be relative to the base url\n\t\t\tnewUrl = baseUrl + unquotedOrigUrl; // already starts with '/'\n\t\t} else {\n\t\t\t// path should be relative to current directory\n\t\t\tnewUrl = currentDir + unquotedOrigUrl.replace(/^\\.\\//, \"\"); // Strip leading './'\n\t\t}\n\n\t\t// send back the fixed url(...)\n\t\treturn \"url(\" + JSON.stringify(newUrl) + \")\";\n\t});\n\n\t// send back the fixed css\n\treturn fixedCss;\n};\n","\nvar content = require(\"!!../../../../node_modules/css-loader/dist/cjs.js!../../../../node_modules/sass-loader/lib/loader.js!./sharebreadcrumb.scss\");\n\nif(typeof content === 'string') content = [[module.id, content, '']];\n\nvar transform;\nvar insertInto;\n\n\n\nvar options = {\"hmr\":true}\n\noptions.transform = transform\noptions.insertInto = undefined;\n\nvar update = require(\"!../../../../node_modules/style-loader/lib/addStyles.js\")(content, options);\n\nif(content.locals) module.exports = content.locals;\n\nif(module.hot) {\n\tmodule.hot.accept(\"!!../../../../node_modules/css-loader/dist/cjs.js!../../../../node_modules/sass-loader/lib/loader.js!./sharebreadcrumb.scss\", function() {\n\t\tvar newContent = require(\"!!../../../../node_modules/css-loader/dist/cjs.js!../../../../node_modules/sass-loader/lib/loader.js!./sharebreadcrumb.scss\");\n\n\t\tif(typeof newContent === 'string') newContent = [[module.id, newContent, '']];\n\n\t\tvar locals = (function(a, b) {\n\t\t\tvar key, idx = 0;\n\n\t\t\tfor(key in a) {\n\t\t\t\tif(!b || a[key] !== b[key]) return false;\n\t\t\t\tidx++;\n\t\t\t}\n\n\t\t\tfor(key in b) idx--;\n\n\t\t\treturn idx === 0;\n\t\t}(content.locals, newContent.locals));\n\n\t\tif(!locals) throw new Error('Aborting CSS HMR due to changed css-modules locals.');\n\n\t\tupdate(newContent);\n\t});\n\n\tmodule.hot.dispose(function() { update(); });\n}","exports = module.exports = require(\"../../../../node_modules/css-loader/dist/runtime/api.js\")(false);\n// Module\nexports.push([module.id, \"/**\\n * @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at>\\n *\\n * @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at>\\n *\\n * @license GNU AGPL version 3 or any later version\\n *\\n * This program is free software: you can redistribute it and/or modify\\n * it under the terms of the GNU Affero General Public License as\\n * published by the Free Software Foundation, either version 3 of the\\n * License, or (at your option) any later version.\\n *\\n * This program is distributed in the hope that it will be useful,\\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\\n * GNU Affero General Public License for more details.\\n *\\n * You should have received a copy of the GNU Affero General Public License\\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\\n *\\n */\\ndiv.crumb span.icon-shared,\\ndiv.crumb span.icon-public {\\n display: inline-block;\\n cursor: pointer;\\n opacity: 0.2;\\n margin-right: 6px; }\\n\\ndiv.crumb span.icon-shared.shared,\\ndiv.crumb span.icon-public.shared {\\n opacity: 0.7; }\\n\", \"\"]);\n\n"],"sourceRoot":""}