Delete the old UI!
This commit is contained in:
parent
33039e9bc4
commit
ff3d8bb013
54 changed files with 308 additions and 4383 deletions
|
@ -1,3 +1,42 @@
|
|||
<span class="entity-reference-element">
|
||||
<span quay-include="{'Config.isNewLayout()': 'directives/new-entity-reference.html', '!Config.isNewLayout()': 'directives/old-entity-reference.html'}"></span>
|
||||
<span class="new-entity-reference" data-title="{{ getTitle(entity) }} {{ entity.name }}" bs-tooltip>
|
||||
<span ng-switch on="entity.kind">
|
||||
<!-- Team -->
|
||||
<span ng-switch-when="team">
|
||||
<span class="avatar" data="entity.avatar" size="avatarSize || 16"></span>
|
||||
<span class="entity-name anchor"
|
||||
href="/organization/{{ namespace }}/teams/{{ entity.name }}"
|
||||
is-only-text="!getIsAdmin(namespace)">
|
||||
{{ entity.name }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<!-- Organization -->
|
||||
<span ng-switch-when="org">
|
||||
<span class="avatar" size="avatarSize || 16" data="entity.avatar"></span>
|
||||
<span class="entity-name anchor" href="/organization/{{ entity.name }}"
|
||||
is-only-text="!getIsAdmin(entity.name)">
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<!-- User or Robot -->
|
||||
<span ng-switch-when="user">
|
||||
<!-- User -->
|
||||
<span ng-if="!entity.is_robot">
|
||||
<span class="avatar" size="avatarSize || 16" data="entity.avatar"></span>
|
||||
<a class="entity-name" href="/user/{{ entity.name }}">{{ entity.name }}</a>
|
||||
</span>
|
||||
|
||||
<!-- Robot -->
|
||||
<span ng-if="entity.is_robot">
|
||||
<i class="fa ci-robot fa-lg"></i>
|
||||
<span class="entity-name anchor" href="{{ getRobotUrl(entity.name) }}"
|
||||
is-only-text="!getIsAdmin(getPrefix(entity.name))">
|
||||
<span class="prefix">{{ getPrefix(entity.name) }}+</span>
|
||||
<span>{{ getShortenedName(entity.name) }}</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
|
|
|
@ -55,12 +55,7 @@
|
|||
<li class="menuitem" role="presentation" ng-repeat="team in teams | orderBy: 'name'" ng-show="!lazyLoading"
|
||||
ng-click="setEntity(team.name, 'team', false, team.avatar)">
|
||||
<a role="menuitem" tabindex="-1" href="javascript:void(0)">
|
||||
<span ng-if="!Config.isNewLayout()">
|
||||
<i class="fa fa-group"></i> <span>{{ team.name }}</span>
|
||||
</span>
|
||||
<span ng-if="Config.isNewLayout()">
|
||||
<span class="avatar" data="team.avatar" size="16"></span> <span>{{ team.name }}</span>
|
||||
</span>
|
||||
<span class="avatar" data="team.avatar" size="16"></span> <span>{{ team.name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
|
|
@ -1,3 +1,207 @@
|
|||
<span class="header-bar-parent">
|
||||
<span quay-include="{'Config.isNewLayout()': 'directives/new-header-bar.html', '!Config.isNewLayout()': 'directives/old-header-bar.html'}"></span>
|
||||
<div class="header-bar-element">
|
||||
<div class="header-bar-content" ng-class="searchVisible ? 'search-visible' : ''">
|
||||
<!-- Quay -->
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
|
||||
≡
|
||||
</button>
|
||||
<a class="navbar-brand" ng-href="{{ user.anonymous ? '/' : '/repository/' }}" target="{{ appLinkTarget() }}">
|
||||
<span id="quay-logo" ng-style="{'background-image': 'url(' + getEnterpriseLogo() + ')'}"></span>
|
||||
</a>
|
||||
<span class="user-tools visible-xs" style="float: right;">
|
||||
<i class="fa fa-search fa-lg user-tool" ng-click="toggleSearch()"
|
||||
data-placement="bottom" data-title="Search" bs-tooltip
|
||||
ng-if="searchingAllowed"></i>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Collapsable stuff -->
|
||||
<div class="collapse navbar-collapse navbar-ex1-collapse">
|
||||
<!-- Not signed in -->
|
||||
<ul class="nav navbar-nav navbar-links" ng-if="user.anonymous">
|
||||
<li><a ng-href="/tour/" target="{{ appLinkTarget() }}" quay-section="tour">Tour</a></li>
|
||||
<li><a ng-href="/tutorial/" target="{{ appLinkTarget() }}" quay-section="tutorial">Tutorial</a></li>
|
||||
<li quay-require="['BILLING']"><a ng-href="/plans/" target="{{ appLinkTarget() }}" quay-section="plans">Pricing</a></li>
|
||||
<li><a href="http://docs.quay.io/" target="_blank">Docs</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Signed in -->
|
||||
<ul class="nav navbar-nav navbar-links" ng-if="!user.anonymous">
|
||||
<li><a ng-href="/repository/" target="{{ appLinkTarget() }}" quay-section="repository">Repositories</a></li>
|
||||
<li><a ng-href="/tutorial/" target="{{ appLinkTarget() }}" quay-section="tutorial">Tutorial</a></li>
|
||||
<li><a href="http://docs.quay.io/" target="_blank">Docs</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Phone -->
|
||||
<ul class="nav navbar-nav navbar-right visible-xs" ng-switch on="user.anonymous">
|
||||
<li ng-switch-when="false">
|
||||
<a href="/user/{{ user.username }}?tab=settings" class="user-view" target="{{ appLinkTarget() }}">
|
||||
<span class="avatar" size="32" data="user.avatar"></span>
|
||||
{{ user.username }}
|
||||
</a>
|
||||
</li>
|
||||
<li ng-switch-default>
|
||||
<a class="user-view" href="/signin/" target="{{ appLinkTarget() }}">Sign in</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Normal -->
|
||||
<ul class="nav navbar-nav navbar-right hidden-xs" ng-switch on="user.anonymous">
|
||||
<li>
|
||||
<span class="navbar-left user-tools">
|
||||
<i class="fa fa-search fa-lg user-tool" ng-click="toggleSearch()"
|
||||
data-placement="bottom" data-title="Search - Keyboard Shortcut: /" bs-tooltip
|
||||
ng-if="searchingAllowed"></i>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="navbar-left user-tools with-menu" ng-show="!user.anonymous">
|
||||
<span class="dropdown">
|
||||
<a href="javascript:void(0)" class="dropdown-toggle new-menu" data-toggle="dropdown">
|
||||
<i class="fa fa-plus user-tool"
|
||||
data-placement="bottom" data-title="Create New..." bs-tooltip></i>
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu context-dropdown">
|
||||
<li>
|
||||
<a href="/organizations/new">
|
||||
<span class="avatar" size="16" data="{'name': '+', color: '#ccc'}"></span>
|
||||
New Organization
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/new{{ getNamespace(currentPageContext) ? '?namespace=' + getNamespace(currentPageContext) : '' }}">
|
||||
<i class="fa fa-hdd-o"></i> New Repository
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation" class="divider" ng-if="getNamespace(currentPageContext) && canAdmin(getNamespace(currentPageContext))"></li>
|
||||
<li role="presentation" class="dropdown-header"
|
||||
ng-if="getNamespace(currentPageContext) && canAdmin(getNamespace(currentPageContext))">
|
||||
Namespace {{ getNamespace(currentPageContext) }}
|
||||
</li>
|
||||
<li ng-if="isOrganization(getNamespace(currentPageContext)) && canAdmin(getNamespace(currentPageContext))">
|
||||
<a href="javascript:void(0)" ng-click="createTeam(currentPageContext)">
|
||||
<i class="fa fa-group"></i> New Team
|
||||
</a>
|
||||
</li>
|
||||
<li ng-if="canAdmin(getNamespace(currentPageContext))">
|
||||
<a href="javascript:void(0)" ng-click="createRobot(currentPageContext)">
|
||||
<i class="fa ci-robot"></i> New Robot Account
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation" class="divider" ng-if="currentPageContext.repository && currentPageContext.repository.can_write"></li>
|
||||
<li role="presentation" class="dropdown-header"
|
||||
ng-if="currentPageContext.repository && currentPageContext.repository.can_write">
|
||||
Repository {{ currentPageContext.repository.namespace }}/{{ currentPageContext.repository.name }}
|
||||
</li>
|
||||
<li ng-if="currentPageContext.repository && currentPageContext.repository.can_write">
|
||||
<a href="javascript:void(0)" ng-click="startBuild()">
|
||||
<i class="fa fa-tasks"></i> New Dockerfile Build
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<span class="navbar-left user-tools" ng-show="!user.anonymous">
|
||||
<a href="javascript:void(0)" data-template="/static/directives/notification-bar.html"
|
||||
data-container="body" data-animation="am-slide-right" bs-aside>
|
||||
<i class="fa fa-bell user-tool"
|
||||
data-placement="bottom" data-title="Notifications" bs-tooltip></i>
|
||||
<span class="notifications-bubble"></span>
|
||||
</a>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="dropdown" ng-switch-when="false">
|
||||
<a href="javascript:void(0)" class="dropdown-toggle user-dropdown user-view" data-toggle="dropdown">
|
||||
<span class="avatar" size="32" data="user.avatar"></span>
|
||||
{{ user.username }}
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a href="/user/{{ user.username }}?tab=settings" target="{{ appLinkTarget() }}">
|
||||
Account Settings
|
||||
</a>
|
||||
</li>
|
||||
<li ng-if="user.super_user"><a href="/superuser/"><strong>Super User Admin Panel</strong></a></li>
|
||||
<li><a href="javascript:void(0)" ng-click="signout()">Sign out</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li ng-switch-default>
|
||||
<a class="user-view" href="/signin/" target="{{ appLinkTarget() }}">Sign in</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</div>
|
||||
|
||||
<div class="search-box" ng-class="getSearchBoxClasses(searchVisible, searchResultState)">
|
||||
<div class="search-label">Search For</div>
|
||||
<div class="search-box-wrapper">
|
||||
<input id="search-box-input" type="search" placeholder="(Enter Search Terms)"
|
||||
ng-model-options="{'debounce': 250}" ng-model="currentSearchQuery"
|
||||
debounce="250"
|
||||
ng-keydown="handleSearchKeyDown($event)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-results"
|
||||
ng-class="searchVisible && searchResultState ? searchResultState.state : ''"
|
||||
ng-class="{'height': (searchResultState.results.length * 40) + 28}">
|
||||
<div class="cor-loader" ng-if="searchResultState.state == 'loading'"></div>
|
||||
<div ng-if="searchResultState.state == 'no-results'">No matching results found</div>
|
||||
<ul ng-if="searchResultState.state == 'results'">
|
||||
<li ng-repeat="result in searchResultState.results" ng-mouseover="setCurrentResult($index)"
|
||||
ng-class="searchResultState.current == $index ? 'current' : ''"
|
||||
ng-click="showResult(result)">
|
||||
<span class="kind">{{ result.kind }}</span>
|
||||
<span class="score" style="display: none">{{ result.score.toString().substr(0, 4) }}</span>
|
||||
<span ng-switch on="result.kind">
|
||||
<!-- Team -->
|
||||
<span ng-switch-when="team">
|
||||
<strong>
|
||||
<span class="avatar" data="result.avatar" size="16"></span>
|
||||
<span class="result-name">{{ result.name }}</span>
|
||||
</strong>
|
||||
<span class="clarification">
|
||||
under organization
|
||||
<span class="avatar" data="result.organization.avatar" size="16"></span>
|
||||
<span class="result-name">{{ result.organization.name }}</span>
|
||||
</span>
|
||||
</span>
|
||||
<span ng-switch-when="user">
|
||||
<span class="avatar" data="result.avatar" size="16"></span>
|
||||
<span class="result-name">{{ result.name }}</span>
|
||||
</span>
|
||||
<span ng-switch-when="organization">
|
||||
<span class="avatar" data="result.avatar" size="16"></span>
|
||||
<span class="result-name">{{ result.name }}</span>
|
||||
</span>
|
||||
<span href="/user/{{ result.name }}" ng-switch-when="robot">
|
||||
<i class="fa ci-robot"></i>
|
||||
<span class="result-name">{{ result.name }}</span>
|
||||
</span>
|
||||
<span ng-switch-when="repository">
|
||||
<span class="avatar" data="result.namespace.avatar" size="16"></span>
|
||||
<span class="result-name">{{ result.namespace.name }}/{{ result.name }}</span>
|
||||
<div class="result-description" ng-if="result.description">
|
||||
<div class="description markdown-view" content="result.description"
|
||||
first-line-only="true" placeholder-needed="false"></div>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="dockerfile-build-dialog"
|
||||
show-now="showBuildDialogCounter"
|
||||
repository="currentPageContext.repository"
|
||||
build-started="handleBuildStarted(build, currentPageContext)">
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
<span class="new-entity-reference" data-title="{{ getTitle(entity) }} {{ entity.name }}" bs-tooltip>
|
||||
<span ng-switch on="entity.kind">
|
||||
<!-- Team -->
|
||||
<span ng-switch-when="team">
|
||||
<span class="avatar" data="entity.avatar" size="avatarSize || 16"></span>
|
||||
<span class="entity-name anchor"
|
||||
href="/organization/{{ namespace }}/teams/{{ entity.name }}"
|
||||
is-only-text="!getIsAdmin(namespace)">
|
||||
{{ entity.name }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<!-- Organization -->
|
||||
<span ng-switch-when="org">
|
||||
<span class="avatar" size="avatarSize || 16" data="entity.avatar"></span>
|
||||
<span class="entity-name anchor" href="/organization/{{ entity.name }}"
|
||||
is-only-text="!getIsAdmin(entity.name)">
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<!-- User or Robot -->
|
||||
<span ng-switch-when="user">
|
||||
<!-- User -->
|
||||
<span ng-if="!entity.is_robot">
|
||||
<span class="avatar" size="avatarSize || 16" data="entity.avatar"></span>
|
||||
<a class="entity-name" href="/user/{{ entity.name }}">{{ entity.name }}</a>
|
||||
</span>
|
||||
|
||||
<!-- Robot -->
|
||||
<span ng-if="entity.is_robot">
|
||||
<i class="fa ci-robot fa-lg"></i>
|
||||
<span class="entity-name anchor" href="{{ getRobotUrl(entity.name) }}"
|
||||
is-only-text="!getIsAdmin(getPrefix(entity.name))">
|
||||
<span class="prefix">{{ getPrefix(entity.name) }}+</span>
|
||||
<span>{{ getShortenedName(entity.name) }}</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
|
@ -1,205 +0,0 @@
|
|||
<div class="header-bar-element">
|
||||
<div class="header-bar-content" ng-class="searchVisible ? 'search-visible' : ''">
|
||||
<!-- Quay -->
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
|
||||
≡
|
||||
</button>
|
||||
<a class="navbar-brand" ng-href="{{ user.anonymous ? '/' : '/repository/' }}" target="{{ appLinkTarget() }}">
|
||||
<span id="quay-logo" ng-style="{'background-image': 'url(' + getEnterpriseLogo() + ')'}"></span>
|
||||
</a>
|
||||
<span class="user-tools visible-xs" style="float: right;">
|
||||
<i class="fa fa-search fa-lg user-tool" ng-click="toggleSearch()"
|
||||
data-placement="bottom" data-title="Search" bs-tooltip
|
||||
ng-if="searchingAllowed"></i>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Collapsable stuff -->
|
||||
<div class="collapse navbar-collapse navbar-ex1-collapse">
|
||||
<!-- Not signed in -->
|
||||
<ul class="nav navbar-nav navbar-links" ng-if="user.anonymous">
|
||||
<li><a ng-href="/tour/" target="{{ appLinkTarget() }}" quay-section="tour">Tour</a></li>
|
||||
<li><a ng-href="/tutorial/" target="{{ appLinkTarget() }}" quay-section="tutorial">Tutorial</a></li>
|
||||
<li quay-require="['BILLING']"><a ng-href="/plans/" target="{{ appLinkTarget() }}" quay-section="plans">Pricing</a></li>
|
||||
<li><a href="http://docs.quay.io/" target="_blank">Docs</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Signed in -->
|
||||
<ul class="nav navbar-nav navbar-links" ng-if="!user.anonymous">
|
||||
<li><a ng-href="/repository/" target="{{ appLinkTarget() }}" quay-section="repository">Repositories</a></li>
|
||||
<li><a ng-href="/tutorial/" target="{{ appLinkTarget() }}" quay-section="tutorial">Tutorial</a></li>
|
||||
<li><a href="http://docs.quay.io/" target="_blank">Docs</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Phone -->
|
||||
<ul class="nav navbar-nav navbar-right visible-xs" ng-switch on="user.anonymous">
|
||||
<li ng-switch-when="false">
|
||||
<a href="/user/{{ user.username }}?tab=settings" class="user-view" target="{{ appLinkTarget() }}">
|
||||
<span class="avatar" size="32" data="user.avatar"></span>
|
||||
{{ user.username }}
|
||||
</a>
|
||||
</li>
|
||||
<li ng-switch-default>
|
||||
<a class="user-view" href="/signin/" target="{{ appLinkTarget() }}">Sign in</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Normal -->
|
||||
<ul class="nav navbar-nav navbar-right hidden-xs" ng-switch on="user.anonymous">
|
||||
<li>
|
||||
<span class="navbar-left user-tools">
|
||||
<i class="fa fa-search fa-lg user-tool" ng-click="toggleSearch()"
|
||||
data-placement="bottom" data-title="Search - Keyboard Shortcut: /" bs-tooltip
|
||||
ng-if="searchingAllowed"></i>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="navbar-left user-tools with-menu" ng-show="!user.anonymous">
|
||||
<span class="dropdown">
|
||||
<a href="javascript:void(0)" class="dropdown-toggle new-menu" data-toggle="dropdown">
|
||||
<i class="fa fa-plus user-tool"
|
||||
data-placement="bottom" data-title="Create New..." bs-tooltip></i>
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu context-dropdown">
|
||||
<li>
|
||||
<a href="/organizations/new">
|
||||
<span class="avatar" size="16" data="{'name': '+', color: '#ccc'}"></span>
|
||||
New Organization
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/new{{ getNamespace(currentPageContext) ? '?namespace=' + getNamespace(currentPageContext) : '' }}">
|
||||
<i class="fa fa-hdd-o"></i> New Repository
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation" class="divider" ng-if="getNamespace(currentPageContext) && canAdmin(getNamespace(currentPageContext))"></li>
|
||||
<li role="presentation" class="dropdown-header"
|
||||
ng-if="getNamespace(currentPageContext) && canAdmin(getNamespace(currentPageContext))">
|
||||
Namespace {{ getNamespace(currentPageContext) }}
|
||||
</li>
|
||||
<li ng-if="isOrganization(getNamespace(currentPageContext)) && canAdmin(getNamespace(currentPageContext))">
|
||||
<a href="javascript:void(0)" ng-click="createTeam(currentPageContext)">
|
||||
<i class="fa fa-group"></i> New Team
|
||||
</a>
|
||||
</li>
|
||||
<li ng-if="canAdmin(getNamespace(currentPageContext))">
|
||||
<a href="javascript:void(0)" ng-click="createRobot(currentPageContext)">
|
||||
<i class="fa ci-robot"></i> New Robot Account
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation" class="divider" ng-if="currentPageContext.repository && currentPageContext.repository.can_write"></li>
|
||||
<li role="presentation" class="dropdown-header"
|
||||
ng-if="currentPageContext.repository && currentPageContext.repository.can_write">
|
||||
Repository {{ currentPageContext.repository.namespace }}/{{ currentPageContext.repository.name }}
|
||||
</li>
|
||||
<li ng-if="currentPageContext.repository && currentPageContext.repository.can_write">
|
||||
<a href="javascript:void(0)" ng-click="startBuild()">
|
||||
<i class="fa fa-tasks"></i> New Dockerfile Build
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<span class="navbar-left user-tools" ng-show="!user.anonymous">
|
||||
<a href="javascript:void(0)" data-template="/static/directives/notification-bar.html"
|
||||
data-container="body" data-animation="am-slide-right" bs-aside>
|
||||
<i class="fa fa-bell user-tool"
|
||||
data-placement="bottom" data-title="Notifications" bs-tooltip></i>
|
||||
<span class="notifications-bubble"></span>
|
||||
</a>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="dropdown" ng-switch-when="false">
|
||||
<a href="javascript:void(0)" class="dropdown-toggle user-dropdown user-view" data-toggle="dropdown">
|
||||
<span class="avatar" size="32" data="user.avatar"></span>
|
||||
{{ user.username }}
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a href="/user/{{ user.username }}?tab=settings" target="{{ appLinkTarget() }}">
|
||||
Account Settings
|
||||
</a>
|
||||
</li>
|
||||
<li ng-if="user.super_user"><a href="/superuser/"><strong>Super User Admin Panel</strong></a></li>
|
||||
<li><a href="javascript:void(0)" ng-click="signout()">Sign out</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li ng-switch-default>
|
||||
<a class="user-view" href="/signin/" target="{{ appLinkTarget() }}">Sign in</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</div>
|
||||
|
||||
<div class="search-box" ng-class="getSearchBoxClasses(searchVisible, searchResultState)">
|
||||
<div class="search-label">Search For</div>
|
||||
<div class="search-box-wrapper">
|
||||
<input id="search-box-input" type="search" placeholder="(Enter Search Terms)"
|
||||
ng-model-options="{'debounce': 250}" ng-model="currentSearchQuery"
|
||||
debounce="250"
|
||||
ng-keydown="handleSearchKeyDown($event)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-results"
|
||||
ng-class="searchVisible && searchResultState ? searchResultState.state : ''"
|
||||
ng-class="{'height': (searchResultState.results.length * 40) + 28}">
|
||||
<div class="cor-loader" ng-if="searchResultState.state == 'loading'"></div>
|
||||
<div ng-if="searchResultState.state == 'no-results'">No matching results found</div>
|
||||
<ul ng-if="searchResultState.state == 'results'">
|
||||
<li ng-repeat="result in searchResultState.results" ng-mouseover="setCurrentResult($index)"
|
||||
ng-class="searchResultState.current == $index ? 'current' : ''"
|
||||
ng-click="showResult(result)">
|
||||
<span class="kind">{{ result.kind }}</span>
|
||||
<span class="score" style="display: none">{{ result.score.toString().substr(0, 4) }}</span>
|
||||
<span ng-switch on="result.kind">
|
||||
<!-- Team -->
|
||||
<span ng-switch-when="team">
|
||||
<strong>
|
||||
<span class="avatar" data="result.avatar" size="16"></span>
|
||||
<span class="result-name">{{ result.name }}</span>
|
||||
</strong>
|
||||
<span class="clarification">
|
||||
under organization
|
||||
<span class="avatar" data="result.organization.avatar" size="16"></span>
|
||||
<span class="result-name">{{ result.organization.name }}</span>
|
||||
</span>
|
||||
</span>
|
||||
<span ng-switch-when="user">
|
||||
<span class="avatar" data="result.avatar" size="16"></span>
|
||||
<span class="result-name">{{ result.name }}</span>
|
||||
</span>
|
||||
<span ng-switch-when="organization">
|
||||
<span class="avatar" data="result.avatar" size="16"></span>
|
||||
<span class="result-name">{{ result.name }}</span>
|
||||
</span>
|
||||
<span href="/user/{{ result.name }}" ng-switch-when="robot">
|
||||
<i class="fa ci-robot"></i>
|
||||
<span class="result-name">{{ result.name }}</span>
|
||||
</span>
|
||||
<span ng-switch-when="repository">
|
||||
<span class="avatar" data="result.namespace.avatar" size="16"></span>
|
||||
<span class="result-name">{{ result.namespace.name }}/{{ result.name }}</span>
|
||||
<div class="result-description" ng-if="result.description">
|
||||
<div class="description markdown-view" content="result.description"
|
||||
first-line-only="true" placeholder-needed="false"></div>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="dockerfile-build-dialog"
|
||||
show-now="showBuildDialogCounter"
|
||||
repository="currentPageContext.repository"
|
||||
build-started="handleBuildStarted(build, currentPageContext)">
|
||||
</div>
|
||||
</div>
|
|
@ -1,17 +0,0 @@
|
|||
<div class="new-role-group btn-group btn-group-sm">
|
||||
<div class="dropdown" style="text-align: left;">
|
||||
<button class="btn btn-default" ng-class="getRoleInfo(currentRole).kind" data-toggle="dropdown"
|
||||
ng-disabled="readOnly">
|
||||
<span ng-if="currentRole" class="role-title">{{ getRoleInfo(currentRole).title }}</span>
|
||||
<span ng-if="!currentRole">(Select)</span>
|
||||
<span class="caret" ng-if="!readOnly"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" ng-class="pullLeft == 'true' ? '' : 'pull-right'" ng-if="!readOnly">
|
||||
<li ng-repeat="roleInfo in fullRoles" ng-class="roleInfo.kind">
|
||||
<a href="javascript:void(0)" ng-click="setRole(roleInfo.id)">{{ roleInfo.title }}
|
||||
<div class="role-help-text">{{ roleInfo.description }}</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
|
@ -1,39 +0,0 @@
|
|||
<!-- DEPRECATED! -->
|
||||
<span class="old-entity-reference">
|
||||
<span ng-if="entity.kind == 'team'">
|
||||
<i class="fa fa-group" data-title="Team" bs-tooltip="tooltip.title" data-container="body"></i>
|
||||
<span class="entity-name">
|
||||
<span ng-if="!getIsAdmin(namespace)">{{entity.name}}</span>
|
||||
<span ng-if="getIsAdmin(namespace)"><a href="/organization/{{ namespace }}/teams/{{ entity.name }}">{{entity.name}}</a></span>
|
||||
</span>
|
||||
</span>
|
||||
<span ng-if="entity.kind == 'org'">
|
||||
<span class="avatar" size="avatarSize || 16" data="entity.avatar"></span>
|
||||
<span class="entity-name">
|
||||
<span ng-if="!getIsAdmin(entity.name)">{{entity.name}}</span>
|
||||
<span ng-if="getIsAdmin(entity.name)"><a href="/organization/{{ entity.name }}">{{entity.name}}</a></span>
|
||||
</span>
|
||||
</span>
|
||||
<span ng-if="entity.kind != 'team' && entity.kind != 'org'">
|
||||
<span class="avatar" size="avatarSize || 16" data="entity.avatar" ng-if="showAvatar == 'true' && entity.avatar"></span>
|
||||
<span ng-if="showAvatar != 'true' || !entity.avatar">
|
||||
<i class="fa fa-user" ng-show="!entity.is_robot" data-title="User" bs-tooltip="tooltip.title" data-container="body"></i>
|
||||
<i class="fa ci-robot" ng-show="entity.is_robot" data-title="Robot Account" bs-tooltip="tooltip.title" data-container="body"></i>
|
||||
</span>
|
||||
|
||||
<span class="entity-name" ng-if="entity.is_robot">
|
||||
<a href="{{ getRobotUrl(entity.name) }}" ng-if="getIsAdmin(getPrefix(entity.name))">
|
||||
<span class="prefix">{{ getPrefix(entity.name) }}+</span><span>{{ getShortenedName(entity.name) }}</span>
|
||||
</a>
|
||||
<span ng-if="!getIsAdmin(getPrefix(entity.name))">
|
||||
<span class="prefix">{{ getPrefix(entity.name) }}+</span><span>{{ getShortenedName(entity.name) }}</span>
|
||||
</span>
|
||||
</span>
|
||||
<span class="entity-name" ng-if="!entity.is_robot">
|
||||
<span>{{getShortenedName(entity.name)}}</span>
|
||||
</span>
|
||||
</span>
|
||||
<i class="fa fa-exclamation-triangle" ng-if="entity.is_org_member === false"
|
||||
data-title="This user is not a member of the organization" bs-tooltip="tooltip.title" data-container="body">
|
||||
</i>
|
||||
</span>
|
|
@ -1,78 +0,0 @@
|
|||
<!-- Quay -->
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse" style="padding: 0px; padding-left: 4px; padding-right: 4px;">
|
||||
≡
|
||||
</button>
|
||||
<a class="navbar-brand" href="/" target="{{ appLinkTarget() }}">
|
||||
<span id="quay-logo" ng-style="{'background-image': 'url(' + getEnterpriseLogo() + ')'}"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Collapsable stuff -->
|
||||
<div class="collapse navbar-collapse navbar-ex1-collapse">
|
||||
<ul class="nav navbar-nav navbar-links">
|
||||
<li><a ng-href="/tour/" target="{{ appLinkTarget() }}" quay-section="tour">Tour</a></li>
|
||||
<li><a ng-href="/repository/" target="{{ appLinkTarget() }}" quay-section="repository">Repositories</a></li>
|
||||
<li><a href="http://docs.quay.io/" target="_blank">Docs</a></li>
|
||||
<li><a ng-href="/tutorial/" target="{{ appLinkTarget() }}" quay-section="tutorial">Tutorial</a></li>
|
||||
<li quay-require="['BILLING']"><a ng-href="/plans/" target="{{ appLinkTarget() }}" quay-section="plans">Pricing</a></li>
|
||||
<li><a ng-href="{{ user.organizations.length ? '/organizations/' : '/tour/organizations/' }}" target="{{ appLinkTarget() }}" quay-section="organization">Organizations</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Phone -->
|
||||
<ul class="nav navbar-nav navbar-right visible-xs" ng-switch on="user.anonymous">
|
||||
<li ng-switch-when="false">
|
||||
<a href="/user/" class="user-view" target="{{ appLinkTarget() }}">
|
||||
<span class="avatar" size="32" data="user.avatar"></span>
|
||||
{{ user.username }}
|
||||
</a>
|
||||
</li>
|
||||
<li ng-switch-default>
|
||||
<a class="user-view" href="/signin/" target="{{ appLinkTarget() }}">Sign in</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Normal -->
|
||||
<ul class="nav navbar-nav navbar-right hidden-xs" ng-switch on="user.anonymous">
|
||||
<li>
|
||||
<form class="navbar-form navbar-left" role="search">
|
||||
<div class="form-group">
|
||||
<span class="repo-search"></span>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<span class="navbar-left user-tools" ng-show="!user.anonymous">
|
||||
<a href="/new/"><i class="fa fa-upload user-tool" bs-tooltip="tooltip.title" data-placement="bottom" data-title="Create new repository" data-container="body"></i></a>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="dropdown" ng-switch-when="false">
|
||||
<a href="javascript:void(0)" class="dropdown-toggle user-dropdown user-view" data-toggle="dropdown">
|
||||
<span class="avatar" size="32" data="user.avatar"></span>
|
||||
{{ user.username }}
|
||||
<span class="notifications-bubble"></span>
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a href="/user/" target="{{ appLinkTarget() }}">
|
||||
Account Settings
|
||||
</a>
|
||||
</li>
|
||||
<li ng-if="notificationService.notifications.length">
|
||||
<a href="javascript:void(0)" data-template="/static/directives/notification-bar.html"
|
||||
data-animation="am-slide-right" bs-aside="aside" data-container="body">
|
||||
Notifications
|
||||
<span class="notifications-bubble"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li><a ng-href="/organizations/" target="{{ appLinkTarget() }}">Organizations</a></li>
|
||||
<li ng-if="user.super_user"><a href="/superuser/"><strong>Super User Admin Panel</strong></a></li>
|
||||
<li><a href="javascript:void(0)" ng-click="signout()">Sign out</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li ng-switch-default>
|
||||
<a class="user-view" href="/signin/" target="{{ appLinkTarget() }}">Sign in</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
|
@ -1,6 +0,0 @@
|
|||
<div class="btn-group btn-group-sm">
|
||||
<button ng-repeat="role in fullRoles"
|
||||
type="button" class="btn" ng-click="setRole(role.id)"
|
||||
ng-class="(currentRole == role.id) ? ('active btn-' + role.kind) : 'btn-default'"
|
||||
ng-disabled="readOnly">{{ role.title }}</button>
|
||||
</div>
|
|
@ -35,16 +35,16 @@
|
|||
|
||||
<table class="cor-table" ng-if="(robots | filter:robotFilter).length">
|
||||
<thead>
|
||||
<td class="caret-col hidden-xs" ng-if="(user || organization.is_admin) && Config.isNewLayout()"></td>
|
||||
<td class="caret-col hidden-xs" ng-if="(user || organization.is_admin)"></td>
|
||||
<td>Robot Account Name</td>
|
||||
<td ng-if="organization && Config.isNewLayout()">Teams</td>
|
||||
<td ng-if="Config.isNewLayout()">Direct Repository Permissions</td>
|
||||
<td ng-if="organization">Teams</td>
|
||||
<td>Direct Repository Permissions</td>
|
||||
<td class="options-col"></td>
|
||||
</thead>
|
||||
|
||||
<tbody ng-repeat="robotInfo in robots | filter:robotFilter | orderBy:getShortenedRobotName" bindonce>
|
||||
<tr ng-class="robotInfo.showing_permissions ? 'open' : 'closed'">
|
||||
<td class="caret-col hidden-xs" bo-if="(user || organization.is_admin) && Config.isNewLayout()">
|
||||
<td class="caret-col hidden-xs" bo-if="(user || organization.is_admin)">
|
||||
<span bo-if="robotInfo.repositories.length > 0" ng-click="showPermissions(robotInfo)">
|
||||
<i class="fa"
|
||||
ng-class="robotInfo.showing_permissions ? 'fa-caret-down' : 'fa-caret-right'"
|
||||
|
@ -58,7 +58,7 @@
|
|||
<span bo-text="getShortenedName(robotInfo.name)"></span>
|
||||
</a>
|
||||
</td>
|
||||
<td bo-if="organization && Config.isNewLayout()">
|
||||
<td bo-if="organization">
|
||||
<span class="empty" bo-if="robotInfo.teams.length == 0">
|
||||
(Not a member of any team)
|
||||
</span>
|
||||
|
@ -71,7 +71,7 @@
|
|||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td bo-if="Config.isNewLayout()">
|
||||
<td>
|
||||
<span class="empty" bo-if="robotInfo.repositories.length == 0">
|
||||
(No direct permissions on any repositories)
|
||||
</span>
|
||||
|
|
|
@ -1 +1,17 @@
|
|||
<span quay-include="{'Config.isNewLayout()': 'directives/new-role-group.html', '!Config.isNewLayout()': 'directives/old-role-group.html'}"></span>
|
||||
<div class="new-role-group btn-group btn-group-sm">
|
||||
<div class="dropdown" style="text-align: left;">
|
||||
<button class="btn btn-default" ng-class="getRoleInfo(currentRole).kind" data-toggle="dropdown"
|
||||
ng-disabled="readOnly">
|
||||
<span ng-if="currentRole" class="role-title">{{ getRoleInfo(currentRole).title }}</span>
|
||||
<span ng-if="!currentRole">(Select)</span>
|
||||
<span class="caret" ng-if="!readOnly"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" ng-class="pullLeft == 'true' ? '' : 'pull-right'" ng-if="!readOnly">
|
||||
<li ng-repeat="roleInfo in fullRoles" ng-class="roleInfo.kind">
|
||||
<a href="javascript:void(0)" ng-click="setRole(roleInfo.id)">{{ roleInfo.title }}
|
||||
<div class="role-help-text">{{ roleInfo.description }}</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
|
@ -84,12 +84,6 @@ quayApp.config(['$routeProvider', '$locationProvider', 'pages', function($routeP
|
|||
// WARNING WARNING WARNING
|
||||
|
||||
var layoutProfile = 'layout';
|
||||
|
||||
// Check for the override flag.
|
||||
if (window.location.search.indexOf('old-ui=1') >= 0) {
|
||||
layoutProfile = 'old-layout';
|
||||
}
|
||||
|
||||
window.console.log('Using layout profile: ' + layoutProfile);
|
||||
|
||||
var routeBuilder = new AngularRouteBuilder($routeProvider, pages, [
|
||||
|
@ -108,18 +102,9 @@ quayApp.config(['$routeProvider', '$locationProvider', 'pages', function($routeP
|
|||
// Image View
|
||||
.route('/repository/:namespace/:name/image/:image', 'image-view')
|
||||
|
||||
// Repo Admin
|
||||
.route('/repository/:namespace/:name/admin', 'repo-admin')
|
||||
|
||||
// Repo Builds
|
||||
.route('/repository/:namespace/:name/build', 'repo-build')
|
||||
|
||||
// Repo Build View
|
||||
.route('/repository/:namespace/:name/build/:buildid', 'build-view')
|
||||
|
||||
// Repo Build Package
|
||||
.route('/repository/:namespace/:name/build/:buildid/buildpack', 'build-package')
|
||||
|
||||
// Repo List
|
||||
.route('/repository/', 'repo-list')
|
||||
|
||||
|
@ -132,24 +117,15 @@ quayApp.config(['$routeProvider', '$locationProvider', 'pages', function($routeP
|
|||
// View Organization
|
||||
.route('/organization/:orgname', 'org-view')
|
||||
|
||||
// Organization Admin
|
||||
.route('/organization/:orgname/admin', 'org-admin')
|
||||
|
||||
// View Organization Team
|
||||
.route('/organization/:orgname/teams/:teamname', 'team-view')
|
||||
|
||||
// Organization Member Logs
|
||||
.route('/organization/:orgname/logs/:membername', 'org-member-logs')
|
||||
|
||||
// Organization View Application
|
||||
.route('/organization/:orgname/application/:clientid', 'manage-application')
|
||||
|
||||
// View User
|
||||
.route('/user/:username', 'user-view')
|
||||
|
||||
// DEPRECATED: User Admin
|
||||
.route('/user/', 'user-admin')
|
||||
|
||||
// Sign In
|
||||
.route('/signin/', 'signin')
|
||||
|
||||
|
@ -189,9 +165,6 @@ quayApp.config(['$routeProvider', '$locationProvider', 'pages', function($routeP
|
|||
// Confirm Invite
|
||||
.route('/confirminvite', 'confirm-invite')
|
||||
|
||||
// Enable/disable experimental layout
|
||||
.route('/__exp/newlayout', 'exp-new-layout')
|
||||
|
||||
// Default: Redirect to the landing page
|
||||
.otherwise({redirectTo: '/'});
|
||||
}]);
|
||||
|
|
|
@ -235,21 +235,6 @@ angular.module("core-ui", [])
|
|||
return directiveDefinitionObject;
|
||||
})
|
||||
|
||||
// TODO(jschorr): Remove this once new layout is in prod.
|
||||
.directive('corTabShim', function() {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 3,
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {},
|
||||
controller: function($rootScope, $scope, $element, $timeout, $location, UIService) {
|
||||
UIService.initializeTabs($scope, $element);
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
})
|
||||
|
||||
.directive('corTabs', function() {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 3,
|
||||
|
|
|
@ -21,14 +21,6 @@ angular.module('quay').directive('entityReference', function () {
|
|||
};
|
||||
|
||||
$scope.getRobotUrl = function(name) {
|
||||
if (Config.isNewLayout()) {
|
||||
return $scope.getNewRobotUrl(name);
|
||||
} else {
|
||||
return $scope.getOldRobotUrl(name);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getNewRobotUrl = function(name) {
|
||||
var namespace = $scope.getPrefix(name);
|
||||
if (!namespace) {
|
||||
return '';
|
||||
|
@ -47,26 +39,6 @@ angular.module('quay').directive('entityReference', function () {
|
|||
return '/organization/' + org['name'] + '?tab=robots&showRobot=' + UtilService.textToSafeHtml(name);
|
||||
};
|
||||
|
||||
// TODO(jschorr): Remove when new layout is in prod.
|
||||
$scope.getOldRobotUrl = function(name) {
|
||||
var namespace = $scope.getPrefix(name);
|
||||
if (!namespace) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!$scope.getIsAdmin(namespace)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var org = UserService.getOrganization(namespace);
|
||||
if (!org) {
|
||||
// This robot is owned by the user.
|
||||
return '/user/?tab=robots&showRobot=' + UtilService.textToSafeHtml(name);
|
||||
}
|
||||
|
||||
return '/organization/' + org['name'] + '/admin?tab=robots&showRobot=' + UtilService.textToSafeHtml(name);
|
||||
};
|
||||
|
||||
$scope.getTitle = function(entity) {
|
||||
if (!entity) { return ''; }
|
||||
|
||||
|
|
|
@ -14,41 +14,36 @@ angular.module('quay').directive('headerBar', function () {
|
|||
},
|
||||
controller: function($rootScope, $scope, $element, $location, $timeout, hotkeys, UserService,
|
||||
PlanService, ApiService, NotificationService, Config, CreateService, Features) {
|
||||
$scope.isNewLayout = Config.isNewLayout();
|
||||
|
||||
var hotkeysAdded = false;
|
||||
var userUpdated = function(cUser) {
|
||||
$scope.searchingAllowed = Features.ANONYMOUS_ACCESS || !cUser.anonymous;
|
||||
|
||||
if (hotkeysAdded) { return; }
|
||||
hotkeysAdded = true;
|
||||
|
||||
if ($scope.isNewLayout) {
|
||||
hotkeysAdded = true;
|
||||
// Register hotkeys.
|
||||
if ($scope.searchingAllowed) {
|
||||
hotkeys.add({
|
||||
combo: '/',
|
||||
description: 'Show search',
|
||||
callback: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$scope.toggleSearch();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Register hotkeys.
|
||||
if ($scope.searchingAllowed) {
|
||||
hotkeys.add({
|
||||
combo: '/',
|
||||
description: 'Show search',
|
||||
callback: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$scope.toggleSearch();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!cUser.anonymous) {
|
||||
hotkeys.add({
|
||||
combo: 'alt+c',
|
||||
description: 'Create new repository',
|
||||
callback: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$location.url('/new');
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!cUser.anonymous) {
|
||||
hotkeys.add({
|
||||
combo: 'alt+c',
|
||||
description: 'Create new repository',
|
||||
callback: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$location.url('/new');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,143 +0,0 @@
|
|||
(function() {
|
||||
/**
|
||||
* Page which displays a build package for a specific build.
|
||||
*/
|
||||
angular.module('quayPages').config(['pages', function(pages) {
|
||||
pages.create('build-package', 'build-package.html', BuildPackageCtrl);
|
||||
}]);
|
||||
|
||||
function BuildPackageCtrl($scope, Restangular, ApiService, DataFileService, $routeParams, $rootScope, $location, $timeout) {
|
||||
var namespace = $routeParams.namespace;
|
||||
var name = $routeParams.name;
|
||||
var buildid = $routeParams.buildid;
|
||||
|
||||
var params = {
|
||||
'repository': namespace + '/' + name,
|
||||
'build_uuid': buildid
|
||||
};
|
||||
|
||||
$scope.initializeTree = function() {
|
||||
if ($scope.drawn) {
|
||||
$scope.tree.notifyResized();
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.drawn = true;
|
||||
$timeout(function() {
|
||||
$scope.tree.draw('file-tree-container');
|
||||
}, 10);
|
||||
};
|
||||
|
||||
var determineDockerfilePath = function() {
|
||||
var dockerfilePath = 'Dockerfile';
|
||||
var dockerfileFolder = ($scope.repobuild['subdirectory'] || '');
|
||||
if (dockerfileFolder[0] == '/') {
|
||||
dockerfileFolder = dockerfileFolder.substr(1);
|
||||
}
|
||||
if (dockerfileFolder && dockerfileFolder[dockerfileFolder.length - 1] != '/') {
|
||||
dockerfileFolder += '/';
|
||||
}
|
||||
dockerfilePath = dockerfileFolder + 'Dockerfile';
|
||||
return dockerfilePath;
|
||||
};
|
||||
|
||||
var processBuildPack = function(uint8array) {
|
||||
var archiveread = function(files) {
|
||||
var getpath = function(file) {
|
||||
return file.path;
|
||||
};
|
||||
|
||||
var findFile = function(path) {
|
||||
for (var i = 0; i < files.length; ++i) {
|
||||
var file = files[i];
|
||||
if (file.path == path) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
$scope.tree = new FileTree($.map(files, getpath));
|
||||
$($scope.tree).bind('fileClicked', function(e) {
|
||||
var file = findFile(e.path);
|
||||
if (file && file.canRead) {
|
||||
saveAs(file.toBlob(), file.name);
|
||||
}
|
||||
});
|
||||
|
||||
var dockerfilePath = determineDockerfilePath();
|
||||
var dockerfile = findFile(dockerfilePath);
|
||||
if (dockerfile && dockerfile.canRead) {
|
||||
DataFileService.blobToString(dockerfile.toBlob(), function(result) {
|
||||
$scope.$apply(function() {
|
||||
$scope.dockerFilePath = dockerfilePath || 'Dockerfile';
|
||||
$scope.dockerFileContents = result;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$scope.loaded = true;
|
||||
};
|
||||
|
||||
var notarchive = function() {
|
||||
DataFileService.arrayToString(uint8array, function(r) {
|
||||
$scope.dockerFilePath = 'Dockerfile';
|
||||
$scope.dockerFileContents = r;
|
||||
$scope.loaded = true;
|
||||
});
|
||||
};
|
||||
|
||||
setTimeout(function() {
|
||||
$scope.$apply(function() {
|
||||
DataFileService.readDataArrayAsPossibleArchive(uint8array, archiveread, notarchive);
|
||||
});
|
||||
}, 0);
|
||||
};
|
||||
|
||||
var downloadBuildPack = function(url) {
|
||||
$scope.downloadProgress = 0;
|
||||
$scope.downloading = true;
|
||||
startDownload(url);
|
||||
};
|
||||
|
||||
var startDownload = function(url) {
|
||||
var onprogress = function(p) {
|
||||
$scope.downloadProgress = p * 100;
|
||||
};
|
||||
|
||||
var onerror = function() {
|
||||
$scope.downloading = false;
|
||||
$scope.downloadError = true;
|
||||
};
|
||||
|
||||
var onloaded = function(uint8array) {
|
||||
$scope.downloading = false;
|
||||
processBuildPack(uint8array);
|
||||
};
|
||||
|
||||
DataFileService.downloadDataFileAsArrayBuffer($scope, url, onprogress, onerror, onloaded);
|
||||
};
|
||||
|
||||
var getBuildInfo = function() {
|
||||
$scope.repository_build = ApiService.getRepoBuildStatus(null, params, true).then(function(resp) {
|
||||
if (!resp['is_writer']) {
|
||||
$rootScope.title = 'Unknown build';
|
||||
$scope.accessDenied = true;
|
||||
return;
|
||||
}
|
||||
|
||||
$rootScope.title = 'Repository Build Pack - ' + resp['display_name'];
|
||||
$scope.repobuild = resp;
|
||||
$scope.repo = {
|
||||
'namespace': namespace,
|
||||
'name': name
|
||||
};
|
||||
|
||||
downloadBuildPack(resp['archive_url']);
|
||||
return resp;
|
||||
});
|
||||
};
|
||||
|
||||
getBuildInfo();
|
||||
}
|
||||
})();
|
|
@ -1,20 +0,0 @@
|
|||
(function() {
|
||||
/**
|
||||
* Experiment enable page: New layout
|
||||
*/
|
||||
angular.module('quayPages').config(['pages', function(pages) {
|
||||
pages.create('exp-new-layout', 'exp-new-layout.html', ExpCtrl, {
|
||||
'newLayout': true
|
||||
});
|
||||
}]);
|
||||
|
||||
function ExpCtrl($scope, CookieService) {
|
||||
$scope.isEnabled = CookieService.get('quay.exp-new-layout') == 'true';
|
||||
|
||||
$scope.setEnabled = function(value) {
|
||||
$scope.isEnabled = value;
|
||||
CookieService.putPermanent('quay.exp-new-layout', value.toString());
|
||||
document.location.reload();
|
||||
};
|
||||
}
|
||||
}());
|
|
@ -7,10 +7,7 @@
|
|||
'newLayout': true,
|
||||
'title': '{{ image.id }}',
|
||||
'description': 'Image {{ image.id }}'
|
||||
}, ['layout'])
|
||||
|
||||
pages.create('image-view', 'old-image-view.html', OldImageViewCtrl, {
|
||||
}, ['old-layout']);
|
||||
})
|
||||
}]);
|
||||
|
||||
function ImageViewCtrl($scope, $routeParams, $rootScope, $timeout, ApiService, ImageMetadataService) {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
});
|
||||
|
||||
UserService.updateUserIn($scope, function(user) {
|
||||
if (!user.anonymous && Config.isNewLayout()) {
|
||||
if (!user.anonymous) {
|
||||
$location.path('/repository');
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -7,12 +7,7 @@
|
|||
'newLayout': true,
|
||||
'title': 'Manage Application {{ application.name }}',
|
||||
'description': 'Manage an OAuth application'
|
||||
}, ['layout']);
|
||||
|
||||
pages.create('manage-application', 'old-manage-application.html', ManageApplicationCtrl, {
|
||||
'title': 'Manage Application {{ application.name }}',
|
||||
'description': 'Manage an OAuth application'
|
||||
}, ['old-layout']);
|
||||
});
|
||||
}]);
|
||||
|
||||
function ManageApplicationCtrl($scope, $routeParams, $rootScope, $location, $timeout, OAuthService, ApiService, UserService, Config) {
|
||||
|
|
|
@ -7,12 +7,7 @@
|
|||
'newLayout': true,
|
||||
'title': 'New Organization',
|
||||
'description': 'Create a new organization to manage teams and permissions'
|
||||
}, ['layout']);
|
||||
|
||||
pages.create('new-organization', 'old-new-organization.html', NewOrgCtrl, {
|
||||
'title': 'New Organization',
|
||||
'description': 'Create a new organization to manage teams and permissions'
|
||||
}, ['old-layout']);
|
||||
});
|
||||
}]);
|
||||
|
||||
function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, PlanService, ApiService, CookieService, Features) {
|
||||
|
|
|
@ -7,12 +7,7 @@
|
|||
'newLayout': true,
|
||||
'title': 'New Repository',
|
||||
'description': 'Create a new Docker repository'
|
||||
}, ['layout'])
|
||||
|
||||
pages.create('new-repo', 'old-new-repo.html', NewRepoCtrl, {
|
||||
'title': 'New Repository',
|
||||
'description': 'Create a new Docker repository'
|
||||
}, ['old-layout']);
|
||||
})
|
||||
}]);
|
||||
|
||||
function NewRepoCtrl($scope, $location, $http, $timeout, UserService, ApiService, PlanService, TriggerService, Features) {
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
(function() {
|
||||
/**
|
||||
* DEPRECATED: Organization admin/settings page.
|
||||
*/
|
||||
angular.module('quayPages').config(['pages', function(pages) {
|
||||
pages.create('org-admin', 'org-admin.html', OrgAdminCtrl);
|
||||
}]);
|
||||
|
||||
function OrgAdminCtrl($rootScope, $scope, $timeout, Restangular, $routeParams, UserService, PlanService, ApiService, Features, UIService) {
|
||||
var orgname = $routeParams.orgname;
|
||||
|
||||
// Load the list of plans.
|
||||
if (Features.BILLING) {
|
||||
PlanService.getPlans(function(plans) {
|
||||
$scope.plans = plans;
|
||||
$scope.plan_map = {};
|
||||
|
||||
for (var i = 0; i < plans.length; ++i) {
|
||||
$scope.plan_map[plans[i].stripeId] = plans[i];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.orgname = orgname;
|
||||
$scope.membersLoading = true;
|
||||
$scope.membersFound = null;
|
||||
$scope.invoiceLoading = true;
|
||||
$scope.logsShown = 0;
|
||||
$scope.invoicesShown = 0;
|
||||
$scope.applicationsShown = 0;
|
||||
$scope.changingOrganization = false;
|
||||
|
||||
$scope.loadLogs = function() {
|
||||
$scope.logsShown++;
|
||||
};
|
||||
|
||||
$scope.loadApplications = function() {
|
||||
$scope.applicationsShown++;
|
||||
};
|
||||
|
||||
$scope.loadInvoices = function() {
|
||||
$scope.invoicesShown++;
|
||||
};
|
||||
|
||||
$scope.planChanged = function(plan) {
|
||||
$scope.hasPaidPlan = plan && plan.price > 0;
|
||||
};
|
||||
|
||||
$scope.$watch('organizationEmail', function(e) {
|
||||
UIService.hidePopover('#changeEmailForm');
|
||||
});
|
||||
|
||||
$scope.changeEmail = function() {
|
||||
UIService.hidePopover('#changeEmailForm');
|
||||
|
||||
$scope.changingOrganization = true;
|
||||
var params = {
|
||||
'orgname': orgname
|
||||
};
|
||||
|
||||
var data = {
|
||||
'email': $scope.organizationEmail
|
||||
};
|
||||
|
||||
ApiService.changeOrganizationDetails(data, params).then(function(org) {
|
||||
$scope.changingOrganization = false;
|
||||
$scope.changeEmailForm.$setPristine();
|
||||
$scope.organization = org;
|
||||
}, function(result) {
|
||||
$scope.changingOrganization = false;
|
||||
UIService.showFormError('#changeEmailForm', result);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.loadMembers = function() {
|
||||
if ($scope.membersFound) { return; }
|
||||
$scope.membersLoading = true;
|
||||
|
||||
var params = {
|
||||
'orgname': orgname
|
||||
};
|
||||
|
||||
ApiService.getOrganizationMembers(null, params).then(function(resp) {
|
||||
var membersArray = [];
|
||||
for (var key in resp.members) {
|
||||
if (resp.members.hasOwnProperty(key)) {
|
||||
membersArray.push(resp.members[key]);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.membersFound = membersArray;
|
||||
$scope.membersLoading = false;
|
||||
});
|
||||
};
|
||||
|
||||
var loadOrganization = function() {
|
||||
$scope.orgResource = ApiService.getOrganizationAsResource({'orgname': orgname}).get(function(org) {
|
||||
if (org && org.is_admin) {
|
||||
$scope.organization = org;
|
||||
$scope.organizationEmail = org.email;
|
||||
$rootScope.title = orgname + ' (Admin)';
|
||||
$rootScope.description = 'Administration page for organization ' + orgname;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Load the organization.
|
||||
loadOrganization();
|
||||
}
|
||||
})();
|
|
@ -1,49 +0,0 @@
|
|||
(function() {
|
||||
/**
|
||||
* Page for displaying the logs of a member in an organization.
|
||||
*/
|
||||
angular.module('quayPages').config(['pages', function(pages) {
|
||||
pages.create('org-member-logs', 'org-member-logs.html', OrgMemberLogsCtrl);
|
||||
}]);
|
||||
|
||||
function OrgMemberLogsCtrl($scope, $routeParams, $rootScope, $timeout, Restangular, ApiService) {
|
||||
var orgname = $routeParams.orgname;
|
||||
var membername = $routeParams.membername;
|
||||
|
||||
$scope.orgname = orgname;
|
||||
$scope.memberInfo = null;
|
||||
$scope.ready = false;
|
||||
|
||||
var loadOrganization = function() {
|
||||
$scope.orgResource = ApiService.getOrganizationAsResource({'orgname': orgname}).get(function(org) {
|
||||
$scope.organization = org;
|
||||
return org;
|
||||
});
|
||||
};
|
||||
|
||||
var loadMemberInfo = function() {
|
||||
var params = {
|
||||
'orgname': orgname,
|
||||
'membername': membername
|
||||
};
|
||||
|
||||
$scope.memberResource = ApiService.getOrganizationMemberAsResource(params).get(function(resp) {
|
||||
$scope.memberInfo = resp.member;
|
||||
|
||||
$rootScope.title = 'Logs for ' + $scope.memberInfo.name + ' (' + $scope.orgname + ')';
|
||||
$rootScope.description = 'Shows all the actions of ' + $scope.memberInfo.username +
|
||||
' under organization ' + $scope.orgname;
|
||||
|
||||
$timeout(function() {
|
||||
$scope.ready = true;
|
||||
});
|
||||
|
||||
return resp.member;
|
||||
});
|
||||
};
|
||||
|
||||
// Load the org info and the member info.
|
||||
loadOrganization();
|
||||
loadMemberInfo();
|
||||
}
|
||||
})();
|
|
@ -7,10 +7,7 @@
|
|||
'newLayout': true,
|
||||
'title': 'Organization {{ organization.name }}',
|
||||
'description': 'Organization {{ organization.name }}'
|
||||
}, ['layout'])
|
||||
|
||||
pages.create('org-view', 'old-org-view.html', OldOrgViewCtrl, {
|
||||
}, ['old-layout']);
|
||||
})
|
||||
}]);
|
||||
|
||||
function OrgViewCtrl($scope, $routeParams, $timeout, ApiService, UIService, AvatarService) {
|
||||
|
|
|
@ -1,399 +0,0 @@
|
|||
(function() {
|
||||
/**
|
||||
* DEPRECATED: Repository admin/settings page.
|
||||
*/
|
||||
angular.module('quayPages').config(['pages', function(pages) {
|
||||
pages.create('repo-admin', 'repo-admin.html', RepoAdminCtrl);
|
||||
}]);
|
||||
|
||||
function RepoAdminCtrl($scope, Restangular, ApiService, KeyService, TriggerService, $routeParams,
|
||||
$rootScope, $location, UserService, Config, Features, ExternalNotificationData, UtilService) {
|
||||
|
||||
var namespace = $routeParams.namespace;
|
||||
var name = $routeParams.name;
|
||||
|
||||
$scope.Features = Features;
|
||||
$scope.TriggerService = TriggerService;
|
||||
$scope.KeyService = KeyService;
|
||||
|
||||
$scope.permissions = {'team': [], 'user': [], 'loading': 2};
|
||||
$scope.logsShown = 0;
|
||||
$scope.deleting = false;
|
||||
|
||||
$scope.permissionCache = {};
|
||||
$scope.showTriggerSetupCounter = 0;
|
||||
|
||||
$scope.getBadgeFormat = function(format, repo) {
|
||||
if (!repo) { return; }
|
||||
|
||||
var imageUrl = Config.getUrl('/repository/' + namespace + '/' + name + '/status');
|
||||
if (!$scope.repo.is_public) {
|
||||
imageUrl += '?token=' + $scope.repo.status_token;
|
||||
}
|
||||
|
||||
var linkUrl = Config.getUrl('/repository/' + namespace + '/' + name);
|
||||
|
||||
switch (format) {
|
||||
case 'svg':
|
||||
return imageUrl;
|
||||
|
||||
case 'md':
|
||||
return '[![Docker Repository on ' + Config.REGISTRY_TITLE_SHORT + '](' + imageUrl +
|
||||
' "Docker Repository on ' + Config.REGISTRY_TITLE_SHORT + '")](' + linkUrl + ')';
|
||||
|
||||
case 'asciidoc':
|
||||
return 'image:' + imageUrl + '["Docker Repository on ' + Config.REGISTRY_TITLE_SHORT + '", link="' + linkUrl + '"]';
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
$scope.buildEntityForPermission = function(name, permission, kind) {
|
||||
var key = name + ':' + kind;
|
||||
if ($scope.permissionCache[key]) {
|
||||
return $scope.permissionCache[key];
|
||||
}
|
||||
|
||||
return $scope.permissionCache[key] = {
|
||||
'kind': kind,
|
||||
'name': name,
|
||||
'is_robot': permission.is_robot,
|
||||
'is_org_member': permission.is_org_member
|
||||
};
|
||||
};
|
||||
|
||||
$scope.loadLogs = function() {
|
||||
$scope.logsShown++;
|
||||
};
|
||||
|
||||
$scope.grantRole = function() {
|
||||
$('#confirmaddoutsideModal').modal('hide');
|
||||
var entity = $scope.currentAddEntity;
|
||||
$scope.addRole(entity.name, 'read', entity.kind, entity.is_org_member)
|
||||
$scope.currentAddEntity = null;
|
||||
};
|
||||
|
||||
$scope.addNewPermission = function(entity) {
|
||||
// Don't allow duplicates.
|
||||
if (!entity || !entity.kind || $scope.permissions[entity.kind][entity.name]) { return; }
|
||||
|
||||
if (entity.is_org_member === false) {
|
||||
$scope.currentAddEntity = entity;
|
||||
$('#confirmaddoutsideModal').modal('show');
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.addRole(entity.name, 'read', entity.kind);
|
||||
};
|
||||
|
||||
$scope.deleteRole = function(entityName, kind) {
|
||||
var errorHandler = ApiService.errorDisplay('Cannot change permission', function(resp) {
|
||||
if (resp.status == 409) {
|
||||
return 'Cannot change permission as you do not have the authority';
|
||||
}
|
||||
});
|
||||
|
||||
var permissionDelete = Restangular.one(UtilService.getRestUrl('repository', namespace, name, 'permissions', kind, entityName));
|
||||
permissionDelete.customDELETE().then(function() {
|
||||
delete $scope.permissions[kind][entityName];
|
||||
}, errorHandler);
|
||||
};
|
||||
|
||||
$scope.addRole = function(entityName, role, kind) {
|
||||
var permission = {
|
||||
'role': role,
|
||||
};
|
||||
|
||||
var permissionPost = Restangular.one(UtilService.getRestUrl('repository', namespace, name, 'permissions', kind, entityName));
|
||||
permissionPost.customPUT(permission).then(function(result) {
|
||||
$scope.permissions[kind][entityName] = result;
|
||||
}, ApiService.errorDisplay('Cannot change permission'));
|
||||
};
|
||||
|
||||
$scope.setRole = function(role, entityName, kind) {
|
||||
var permission = $scope.permissions[kind][entityName];
|
||||
var currentRole = permission.role;
|
||||
permission.role = role;
|
||||
|
||||
var permissionPut = Restangular.one(UtilService.getRestUrl('repository', namespace, name, 'permissions', kind, entityName));
|
||||
permissionPut.customPUT(permission).then(function() {}, function(resp) {
|
||||
$scope.permissions[kind][entityName] = {'role': currentRole};
|
||||
$scope.changePermError = null;
|
||||
if (resp.status == 409 || resp.data) {
|
||||
$scope.changePermError = resp.data || '';
|
||||
$('#channgechangepermModal').modal({});
|
||||
} else {
|
||||
$('#cannotchangeModal').modal({});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.newTokenName = null;
|
||||
|
||||
$scope.createToken = function() {
|
||||
var data = {
|
||||
'friendlyName': $scope.newTokenName
|
||||
};
|
||||
|
||||
var params = {'repository': namespace + '/' + name};
|
||||
ApiService.createToken(data, params).then(function(newToken) {
|
||||
$scope.newTokenName = null;
|
||||
$scope.createTokenForm.$setPristine();
|
||||
$scope.tokens[newToken.code] = newToken;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.deleteToken = function(tokenCode) {
|
||||
var params = {
|
||||
'repository': namespace + '/' + name,
|
||||
'code': tokenCode
|
||||
};
|
||||
|
||||
ApiService.deleteToken(null, params).then(function() {
|
||||
delete $scope.tokens[tokenCode];
|
||||
});
|
||||
};
|
||||
|
||||
$scope.changeTokenAccess = function(tokenCode, newAccess) {
|
||||
var role = {
|
||||
'role': newAccess
|
||||
};
|
||||
|
||||
var params = {
|
||||
'repository': namespace + '/' + name,
|
||||
'code': tokenCode
|
||||
};
|
||||
|
||||
ApiService.changeToken(role, params).then(function(updated) {
|
||||
$scope.tokens[updated.code] = updated;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.shownTokenCounter = 0;
|
||||
|
||||
$scope.showToken = function(tokenCode) {
|
||||
$scope.shownToken = $scope.tokens[tokenCode];
|
||||
$scope.shownTokenCounter++;
|
||||
};
|
||||
|
||||
$scope.askChangeAccess = function(newAccess) {
|
||||
$('#make' + newAccess + 'Modal').modal({});
|
||||
};
|
||||
|
||||
$scope.changeAccess = function(newAccess) {
|
||||
$('#make' + newAccess + 'Modal').modal('hide');
|
||||
|
||||
var visibility = {
|
||||
'visibility': newAccess
|
||||
};
|
||||
|
||||
var params = {
|
||||
'repository': namespace + '/' + name
|
||||
};
|
||||
|
||||
ApiService.changeRepoVisibility(visibility, params).then(function() {
|
||||
$scope.repo.is_public = newAccess == 'public';
|
||||
}, function() {
|
||||
$('#cannotchangeModal').modal({});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.askDelete = function() {
|
||||
$('#confirmdeleteModal').modal({});
|
||||
};
|
||||
|
||||
$scope.deleteRepo = function() {
|
||||
$('#confirmdeleteModal').modal('hide');
|
||||
|
||||
var params = {
|
||||
'repository': namespace + '/' + name
|
||||
};
|
||||
|
||||
$scope.deleting = true;
|
||||
ApiService.deleteRepository(null, params).then(function() {
|
||||
$scope.repo = null;
|
||||
|
||||
setTimeout(function() {
|
||||
document.location = '/repository/';
|
||||
}, 1000);
|
||||
}, function() {
|
||||
$scope.deleting = true;
|
||||
$('#cannotchangeModal').modal({});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.showNewNotificationCounter = 0;
|
||||
|
||||
$scope.showNewNotificationDialog = function() {
|
||||
$scope.showNewNotificationCounter++;
|
||||
};
|
||||
|
||||
$scope.handleNotificationCreated = function(notification) {
|
||||
$scope.notifications.push(notification);
|
||||
};
|
||||
|
||||
$scope.handleNotificationDeleted = function(notification) {
|
||||
var index = $.inArray(notification, $scope.notifications);
|
||||
if (index < 0) { return; }
|
||||
$scope.notifications.splice(index, 1);
|
||||
};
|
||||
|
||||
$scope.loadNotifications = function() {
|
||||
var params = {
|
||||
'repository': namespace + '/' + name
|
||||
};
|
||||
|
||||
$scope.notificationsResource = ApiService.listRepoNotificationsAsResource(params).get(
|
||||
function(resp) {
|
||||
$scope.notifications = resp.notifications;
|
||||
return $scope.notifications;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.showBuild = function(buildInfo) {
|
||||
$location.path('/repository/' + namespace + '/' + name + '/build');
|
||||
$location.search('current', buildInfo.id);
|
||||
};
|
||||
|
||||
$scope.loadTriggerBuildHistory = function(trigger) {
|
||||
trigger.$loadingHistory = true;
|
||||
|
||||
var params = {
|
||||
'repository': namespace + '/' + name,
|
||||
'trigger_uuid': trigger.id,
|
||||
'limit': 3
|
||||
};
|
||||
|
||||
ApiService.listTriggerRecentBuilds(null, params).then(function(resp) {
|
||||
trigger.$builds = resp['builds'];
|
||||
trigger.$loadingHistory = false;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.loadTriggers = function() {
|
||||
var params = {
|
||||
'repository': namespace + '/' + name
|
||||
};
|
||||
|
||||
$scope.triggersResource = ApiService.listBuildTriggersAsResource(params).get(function(resp) {
|
||||
$scope.triggers = resp.triggers;
|
||||
|
||||
// Check to see if we need to setup any trigger.
|
||||
var newTriggerId = $routeParams.new_trigger;
|
||||
if (newTriggerId) {
|
||||
for (var i = 0; i < $scope.triggers.length; ++i) {
|
||||
var trigger = $scope.triggers[i];
|
||||
if (trigger['id'] == newTriggerId && !trigger['is_active']) {
|
||||
$scope.setupTrigger(trigger);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $scope.triggers;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.setupTrigger = function(trigger) {
|
||||
$scope.currentSetupTrigger = trigger;
|
||||
$scope.showTriggerSetupCounter++;
|
||||
};
|
||||
|
||||
$scope.cancelSetupTrigger = function(trigger) {
|
||||
if ($scope.currentSetupTrigger != trigger) { return; }
|
||||
|
||||
$scope.currentSetupTrigger = null;
|
||||
$scope.deleteTrigger(trigger);
|
||||
};
|
||||
|
||||
$scope.showManualBuildDialog = 0;
|
||||
|
||||
$scope.startTrigger = function(trigger, opt_custom) {
|
||||
var parameters = TriggerService.getRunParameters(trigger.service);
|
||||
if (parameters.length && !opt_custom) {
|
||||
$scope.currentStartTrigger = trigger;
|
||||
$scope.showManualBuildDialog++;
|
||||
return;
|
||||
}
|
||||
|
||||
var params = {
|
||||
'repository': namespace + '/' + name,
|
||||
'trigger_uuid': trigger.id
|
||||
};
|
||||
|
||||
ApiService.manuallyStartBuildTrigger(opt_custom || {}, params).then(function(resp) {
|
||||
var url = '/repository/' + namespace + '/' + name + '/build?current=' + resp['id'];
|
||||
document.location = url;
|
||||
}, ApiService.errorDisplay('Could not start build'));
|
||||
};
|
||||
|
||||
$scope.deleteTrigger = function(trigger) {
|
||||
if (!trigger) { return; }
|
||||
|
||||
var params = {
|
||||
'repository': namespace + '/' + name,
|
||||
'trigger_uuid': trigger.id
|
||||
};
|
||||
|
||||
ApiService.deleteBuildTrigger(null, params).then(function(resp) {
|
||||
$scope.triggers.splice($scope.triggers.indexOf(trigger), 1);
|
||||
});
|
||||
};
|
||||
|
||||
var fetchTokens = function() {
|
||||
var params = {
|
||||
'repository': namespace + '/' + name
|
||||
};
|
||||
|
||||
ApiService.listRepoTokens(null, params).then(function(resp) {
|
||||
$scope.tokens = resp.tokens;
|
||||
}, function() {
|
||||
$scope.tokens = null;
|
||||
});
|
||||
};
|
||||
|
||||
var fetchPermissions = function(kind) {
|
||||
var permissionsFetch = Restangular.one('repository/' + namespace + '/' + name + '/permissions/' + kind + '/');
|
||||
permissionsFetch.get().then(function(resp) {
|
||||
$scope.permissions[kind] = resp.permissions;
|
||||
$scope.permissions['loading']--;
|
||||
}, function() {
|
||||
$scope.permissions[kind] = null;
|
||||
});
|
||||
};
|
||||
|
||||
var fetchRepository = function() {
|
||||
var params = {
|
||||
'repository': namespace + '/' + name
|
||||
};
|
||||
|
||||
$scope.repository = ApiService.getRepoAsResource(params).get(function(repo) {
|
||||
if (!repo.can_admin) {
|
||||
$rootScope.title = 'Forbidden';
|
||||
$scope.accessDenied = true;
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.repo = repo;
|
||||
$rootScope.title = 'Settings - ' + namespace + '/' + name;
|
||||
$rootScope.description = 'Administrator settings for ' + namespace + '/' + name +
|
||||
': Permissions, notifications and other settings';
|
||||
|
||||
// Fetch all the permissions and token info for the repository.
|
||||
fetchPermissions('user');
|
||||
fetchPermissions('team');
|
||||
fetchTokens();
|
||||
|
||||
$('.info-icon').popover({
|
||||
'trigger': 'hover',
|
||||
'html': true
|
||||
});
|
||||
|
||||
return $scope.repo;
|
||||
});
|
||||
};
|
||||
|
||||
// Fetch the repository.
|
||||
fetchRepository();
|
||||
}
|
||||
})();
|
|
@ -1,294 +0,0 @@
|
|||
(function() {
|
||||
/**
|
||||
* Repository Build view page. Displays the status of a repository build.
|
||||
*/
|
||||
angular.module('quayPages').config(['pages', function(pages) {
|
||||
pages.create('repo-build', 'repo-build.html', RepoBuildCtrl);
|
||||
}]);
|
||||
|
||||
function RepoBuildCtrl($scope, Restangular, ApiService, $routeParams, $rootScope, $location, $interval, $sanitize,
|
||||
ansi2html, AngularViewArray, AngularPollChannel) {
|
||||
var namespace = $routeParams.namespace;
|
||||
var name = $routeParams.name;
|
||||
|
||||
// Watch for changes to the current parameter.
|
||||
$scope.$on('$routeUpdate', function(){
|
||||
if ($location.search().current) {
|
||||
$scope.setCurrentBuild($location.search().current, false);
|
||||
}
|
||||
});
|
||||
|
||||
$scope.builds = null;
|
||||
$scope.pollChannel = null;
|
||||
$scope.buildDialogShowCounter = 0;
|
||||
|
||||
$scope.showNewBuildDialog = function() {
|
||||
$scope.buildDialogShowCounter++;
|
||||
};
|
||||
|
||||
$scope.handleBuildStarted = function(newBuild) {
|
||||
if (!$scope.builds) { return; }
|
||||
|
||||
$scope.builds.unshift(newBuild);
|
||||
$scope.setCurrentBuild(newBuild['id'], true);
|
||||
};
|
||||
|
||||
$scope.adjustLogHeight = function() {
|
||||
var triggerOffset = 0;
|
||||
if ($scope.currentBuild && $scope.currentBuild.trigger) {
|
||||
triggerOffset = 85;
|
||||
}
|
||||
$('.build-logs').height($(window).height() - 415 - triggerOffset);
|
||||
};
|
||||
|
||||
$scope.askRestartBuild = function(build) {
|
||||
$('#confirmRestartBuildModal').modal({});
|
||||
};
|
||||
|
||||
$scope.askCancelBuild = function(build) {
|
||||
bootbox.confirm('Are you sure you want to cancel this build?', function(r) {
|
||||
if (r) {
|
||||
var params = {
|
||||
'repository': namespace + '/' + name,
|
||||
'build_uuid': build.id
|
||||
};
|
||||
|
||||
ApiService.cancelRepoBuild(null, params).then(function() {
|
||||
if (!$scope.builds) { return; }
|
||||
$scope.builds.splice($.inArray(build, $scope.builds), 1);
|
||||
|
||||
if ($scope.builds.length) {
|
||||
$scope.currentBuild = $scope.builds[0];
|
||||
} else {
|
||||
$scope.currentBuild = null;
|
||||
}
|
||||
}, ApiService.errorDisplay('Cannot cancel build'));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.restartBuild = function(build) {
|
||||
$('#confirmRestartBuildModal').modal('hide');
|
||||
|
||||
var subdirectory = build['subdirectory'] || '';
|
||||
|
||||
var data = {
|
||||
'file_id': build['resource_key'],
|
||||
'subdirectory': subdirectory,
|
||||
'docker_tags': build['tags']
|
||||
};
|
||||
|
||||
if (build['pull_robot']) {
|
||||
data['pull_robot'] = build['pull_robot']['name'];
|
||||
}
|
||||
|
||||
var params = {
|
||||
'repository': namespace + '/' + name
|
||||
};
|
||||
|
||||
ApiService.requestRepoBuild(data, params).then(function(newBuild) {
|
||||
if (!$scope.builds) { return; }
|
||||
|
||||
$scope.builds.unshift(newBuild);
|
||||
$scope.setCurrentBuild(newBuild['id'], true);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.hasLogs = function(container) {
|
||||
return container.logs.hasEntries;
|
||||
};
|
||||
|
||||
$scope.setCurrentBuild = function(buildId, opt_updateURL) {
|
||||
if (!$scope.builds) { return; }
|
||||
|
||||
// Find the build.
|
||||
for (var i = 0; i < $scope.builds.length; ++i) {
|
||||
if ($scope.builds[i].id == buildId) {
|
||||
$scope.setCurrentBuildInternal(i, $scope.builds[i], opt_updateURL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.processANSI = function(message, container) {
|
||||
var filter = container.logs._filter = (container.logs._filter || ansi2html.create());
|
||||
|
||||
// Note: order is important here.
|
||||
var setup = filter.getSetupHtml();
|
||||
var stream = filter.addInputToStream(message);
|
||||
var teardown = filter.getTeardownHtml();
|
||||
return setup + stream + teardown;
|
||||
};
|
||||
|
||||
$scope.setCurrentBuildInternal = function(index, build, opt_updateURL) {
|
||||
if (build == $scope.currentBuild) { return; }
|
||||
|
||||
$scope.logEntries = null;
|
||||
$scope.logStartIndex = null;
|
||||
$scope.currentParentEntry = null;
|
||||
|
||||
$scope.currentBuild = build;
|
||||
|
||||
if (opt_updateURL) {
|
||||
if (build) {
|
||||
$location.search('current', build.id);
|
||||
} else {
|
||||
$location.search('current', null);
|
||||
}
|
||||
}
|
||||
|
||||
// Timeout needed to ensure the log element has been created
|
||||
// before its height is adjusted.
|
||||
setTimeout(function() {
|
||||
$scope.adjustLogHeight();
|
||||
}, 1);
|
||||
|
||||
// Stop any existing polling.
|
||||
if ($scope.pollChannel) {
|
||||
$scope.pollChannel.stop();
|
||||
}
|
||||
|
||||
// Create a new channel for polling the build status and logs.
|
||||
var conductStatusAndLogRequest = function(callback) {
|
||||
getBuildStatusAndLogs(build, callback);
|
||||
};
|
||||
|
||||
$scope.pollChannel = AngularPollChannel.create($scope, conductStatusAndLogRequest, 5 * 1000 /* 5s */);
|
||||
$scope.pollChannel.start();
|
||||
};
|
||||
|
||||
var processLogs = function(logs, startIndex, endIndex) {
|
||||
if (!$scope.logEntries) { $scope.logEntries = []; }
|
||||
|
||||
// If the start index given is less than that requested, then we've received a larger
|
||||
// pool of logs, and we need to only consider the new ones.
|
||||
if (startIndex < $scope.logStartIndex) {
|
||||
logs = logs.slice($scope.logStartIndex - startIndex);
|
||||
}
|
||||
|
||||
for (var i = 0; i < logs.length; ++i) {
|
||||
var entry = logs[i];
|
||||
var type = entry['type'] || 'entry';
|
||||
if (type == 'command' || type == 'phase' || type == 'error') {
|
||||
entry['logs'] = AngularViewArray.create();
|
||||
entry['index'] = $scope.logStartIndex + i;
|
||||
|
||||
$scope.logEntries.push(entry);
|
||||
$scope.currentParentEntry = entry;
|
||||
} else if ($scope.currentParentEntry) {
|
||||
$scope.currentParentEntry['logs'].push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return endIndex;
|
||||
};
|
||||
|
||||
var handleLogsData = function(logsData, callback) {
|
||||
// Process the logs we've received.
|
||||
$scope.logStartIndex = processLogs(logsData['logs'], logsData['start'], logsData['total']);
|
||||
|
||||
// If the build status is an error, open the last two log entries.
|
||||
var currentBuild = $scope.currentBuild;
|
||||
if (currentBuild['phase'] == 'error' && $scope.logEntries.length > 1) {
|
||||
var openLogEntries = function(entry) {
|
||||
if (entry.logs) {
|
||||
entry.logs.setVisible(true);
|
||||
}
|
||||
};
|
||||
|
||||
openLogEntries($scope.logEntries[$scope.logEntries.length - 2]);
|
||||
openLogEntries($scope.logEntries[$scope.logEntries.length - 1]);
|
||||
}
|
||||
|
||||
// If the build phase is an error or a complete, then we mark the channel
|
||||
// as closed.
|
||||
callback(currentBuild['phase'] != 'error' && currentBuild['phase'] != 'complete');
|
||||
};
|
||||
|
||||
var getBuildStatusAndLogs = function(build, callback) {
|
||||
var params = {
|
||||
'repository': namespace + '/' + name,
|
||||
'build_uuid': build.id
|
||||
};
|
||||
|
||||
ApiService.getRepoBuildStatus(null, params, true).then(function(resp) {
|
||||
if (build != $scope.currentBuild) { callback(false); return; }
|
||||
|
||||
// Note: We use extend here rather than replacing as Angular is depending on the
|
||||
// root build object to remain the same object.
|
||||
var matchingBuilds = $.grep($scope.builds, function(elem) {
|
||||
return elem['id'] == resp['id']
|
||||
});
|
||||
|
||||
var currentBuild = matchingBuilds.length > 0 ? matchingBuilds[0] : null;
|
||||
if (currentBuild) {
|
||||
currentBuild = $.extend(true, currentBuild, resp);
|
||||
} else {
|
||||
currentBuild = resp;
|
||||
$scope.builds.push(currentBuild);
|
||||
}
|
||||
|
||||
// Load the updated logs for the build.
|
||||
var options = {
|
||||
'start': $scope.logStartIndex
|
||||
};
|
||||
|
||||
ApiService.getRepoBuildLogsAsResource(params, true).withOptions(options).get(function(resp) {
|
||||
if (build != $scope.currentBuild) { callback(false); return; }
|
||||
|
||||
// If we get a logs url back, then we need to make another XHR request to retrieve the
|
||||
// data.
|
||||
if (resp['logs_url']) {
|
||||
$.ajax({
|
||||
url: resp['logs_url'],
|
||||
}).done(function(r) {
|
||||
handleLogsData(r, callback);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
handleLogsData(resp, callback);
|
||||
}, function() {
|
||||
callback(false);
|
||||
});
|
||||
}, function() {
|
||||
callback(false);
|
||||
});
|
||||
};
|
||||
|
||||
var fetchRepository = function() {
|
||||
var params = {'repository': namespace + '/' + name};
|
||||
$rootScope.title = 'Loading Repository...';
|
||||
$scope.repository = ApiService.getRepoAsResource(params).get(function(repo) {
|
||||
if (!repo.can_write) {
|
||||
$rootScope.title = 'Unknown builds';
|
||||
$scope.accessDenied = true;
|
||||
return;
|
||||
}
|
||||
|
||||
$rootScope.title = 'Repository Builds';
|
||||
$scope.repo = repo;
|
||||
|
||||
getBuildInfo();
|
||||
});
|
||||
};
|
||||
|
||||
var getBuildInfo = function(repo) {
|
||||
var params = {
|
||||
'repository': namespace + '/' + name
|
||||
};
|
||||
|
||||
ApiService.getRepoBuilds(null, params).then(function(resp) {
|
||||
$scope.builds = resp.builds;
|
||||
|
||||
if ($location.search().current) {
|
||||
$scope.setCurrentBuild($location.search().current, false);
|
||||
} else if ($scope.builds.length > 0) {
|
||||
$scope.setCurrentBuild($scope.builds[0].id, true);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
fetchRepository();
|
||||
}
|
||||
})();
|
|
@ -7,12 +7,7 @@
|
|||
'newLayout': true,
|
||||
'title': 'Repositories',
|
||||
'description': 'View and manage Docker repositories'
|
||||
}, ['layout'])
|
||||
|
||||
pages.create('repo-list', 'old-repo-list.html', OldRepoListCtrl, {
|
||||
'title': 'Repositories',
|
||||
'description': 'View and manage Docker repositories'
|
||||
}, ['old-layout']);
|
||||
})
|
||||
}]);
|
||||
|
||||
|
||||
|
|
|
@ -7,10 +7,7 @@
|
|||
'newLayout': true,
|
||||
'title': '{{ namespace }}/{{ name }}',
|
||||
'description': 'Repository {{ namespace }}/{{ name }}'
|
||||
}, ['layout'])
|
||||
|
||||
pages.create('repo-view', 'old-repo-view.html', OldRepoViewCtrl, {
|
||||
}, ['old-layout']);
|
||||
})
|
||||
}]);
|
||||
|
||||
function RepoViewCtrl($scope, $routeParams, $location, $timeout, ApiService, UserService, AngularPollChannel) {
|
||||
|
|
|
@ -8,10 +8,7 @@
|
|||
{
|
||||
'newLayout': true,
|
||||
'title': 'Enterprise Registry Setup'
|
||||
},
|
||||
|
||||
// Note: This page has already been converted, but also needs to be available in the old layout
|
||||
['layout', 'old-layout'])
|
||||
})
|
||||
}]);
|
||||
|
||||
function SetupCtrl($scope, $timeout, ApiService, Features, UserService, ContainerService, CoreDialog) {
|
||||
|
|
|
@ -7,10 +7,7 @@
|
|||
{
|
||||
'newLayout': true,
|
||||
'title': 'Enterprise Registry Management'
|
||||
},
|
||||
|
||||
// Note: This page has already been converted, but also needs to be available in the old layout
|
||||
['layout', 'old-layout'])
|
||||
})
|
||||
}]);
|
||||
|
||||
function SuperuserCtrl($scope, $timeout, ApiService, Features, UserService, ContainerService, AngularPollChannel, CoreDialog) {
|
||||
|
|
|
@ -7,10 +7,7 @@
|
|||
'newLayout': true,
|
||||
'title': 'Team {{ teamname }}',
|
||||
'description': 'Team {{ teamname }}'
|
||||
}, ['layout'])
|
||||
|
||||
pages.create('team-view', 'old-team-view.html', TeamViewCtrl, {
|
||||
}, ['old-layout']);
|
||||
})
|
||||
}]);
|
||||
|
||||
function TeamViewCtrl($rootScope, $scope, $timeout, Features, Restangular, ApiService, $routeParams) {
|
||||
|
|
|
@ -7,10 +7,7 @@
|
|||
'newLayout': true,
|
||||
'title': 'Tutorial',
|
||||
'description': 'Basic tutorial on using Quay.io'
|
||||
}, ['layout'])
|
||||
|
||||
pages.create('tutorial', 'old-tutorial.html', TutorialCtrl, {
|
||||
}, ['old-layout']);
|
||||
})
|
||||
}]);
|
||||
|
||||
function TutorialCtrl($scope, AngularTour, AngularTourSignals, UserService, Config, Features) {
|
||||
|
|
|
@ -1,222 +0,0 @@
|
|||
(function() {
|
||||
/**
|
||||
* DEPRECATED: User admin/settings page.
|
||||
*/
|
||||
angular.module('quayPages').config(['pages', function(pages) {
|
||||
pages.create('user-admin', 'user-admin.html', UserAdminCtrl, {
|
||||
'title': 'User Settings'
|
||||
});
|
||||
}]);
|
||||
|
||||
function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, UserService, CookieService, KeyService,
|
||||
$routeParams, $http, UIService, Features, Config) {
|
||||
$scope.Features = Features;
|
||||
|
||||
if ($routeParams['migrate']) {
|
||||
$('#migrateTab').tab('show')
|
||||
}
|
||||
|
||||
UserService.updateUserIn($scope, function(user) {
|
||||
$scope.cuser = jQuery.extend({}, user);
|
||||
|
||||
if ($scope.cuser.logins) {
|
||||
for (var i = 0; i < $scope.cuser.logins.length; i++) {
|
||||
var login = $scope.cuser.logins[i];
|
||||
login.metadata = login.metadata || {};
|
||||
|
||||
if (login.service == 'github') {
|
||||
$scope.hasGithubLogin = true;
|
||||
$scope.githubLogin = login.metadata['service_username'];
|
||||
$scope.githubEndpoint = KeyService['githubEndpoint'];
|
||||
}
|
||||
|
||||
if (login.service == 'google') {
|
||||
$scope.hasGoogleLogin = true;
|
||||
$scope.googleLogin = login.metadata['service_username'];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$scope.readyForPlan = function() {
|
||||
// Show the subscribe dialog if a plan was requested.
|
||||
return $routeParams['plan'];
|
||||
};
|
||||
|
||||
$scope.loading = true;
|
||||
$scope.updatingUser = false;
|
||||
$scope.changePasswordSuccess = false;
|
||||
$scope.changeEmailSent = false;
|
||||
$scope.convertStep = 0;
|
||||
$scope.org = {};
|
||||
$scope.githubRedirectUri = KeyService.githubRedirectUri;
|
||||
$scope.authorizedApps = null;
|
||||
|
||||
$scope.logsShown = 0;
|
||||
$scope.invoicesShown = 0;
|
||||
|
||||
$scope.USER_PATTERN = USER_PATTERN;
|
||||
|
||||
$scope.loadAuthedApps = function() {
|
||||
if ($scope.authorizedApps) { return; }
|
||||
|
||||
ApiService.listUserAuthorizations().then(function(resp) {
|
||||
$scope.authorizedApps = resp['authorizations'];
|
||||
});
|
||||
};
|
||||
|
||||
$scope.deleteAccess = function(accessTokenInfo) {
|
||||
var params = {
|
||||
'access_token_uuid': accessTokenInfo['uuid']
|
||||
};
|
||||
|
||||
ApiService.deleteUserAuthorization(null, params).then(function(resp) {
|
||||
$scope.authorizedApps.splice($scope.authorizedApps.indexOf(accessTokenInfo), 1);
|
||||
}, ApiService.errorDisplay('Could not revoke authorization'));
|
||||
};
|
||||
|
||||
$scope.loadLogs = function() {
|
||||
if (!$scope.hasPaidBusinessPlan) { return; }
|
||||
$scope.logsShown++;
|
||||
};
|
||||
|
||||
$scope.loadInvoices = function() {
|
||||
$scope.invoicesShown++;
|
||||
};
|
||||
|
||||
$scope.planChanged = function(plan) {
|
||||
$scope.hasPaidPlan = plan && plan.price > 0;
|
||||
$scope.hasPaidBusinessPlan = PlanService.isOrgCompatible(plan) && plan.price > 0;
|
||||
};
|
||||
|
||||
$scope.showConvertForm = function() {
|
||||
if (Features.BILLING) {
|
||||
PlanService.getMatchingBusinessPlan(function(plan) {
|
||||
$scope.org.plan = plan;
|
||||
});
|
||||
|
||||
PlanService.getPlans(function(plans) {
|
||||
$scope.orgPlans = plans;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.convertStep = 1;
|
||||
};
|
||||
|
||||
$scope.convertToOrg = function() {
|
||||
$('#reallyconvertModal').modal({});
|
||||
};
|
||||
|
||||
$scope.reallyConvert = function() {
|
||||
if (Config.AUTHENTICATION_TYPE != 'Database') { return; }
|
||||
|
||||
$scope.loading = true;
|
||||
|
||||
var data = {
|
||||
'adminUser': $scope.org.adminUser,
|
||||
'adminPassword': $scope.org.adminPassword,
|
||||
'plan': $scope.org.plan ? $scope.org.plan.stripeId : ''
|
||||
};
|
||||
|
||||
ApiService.convertUserToOrganization(data).then(function(resp) {
|
||||
CookieService.putPermanent('quay.namespace', $scope.cuser.username);
|
||||
UserService.load();
|
||||
$location.path('/');
|
||||
}, function(resp) {
|
||||
$scope.loading = false;
|
||||
if (resp.data.reason == 'invaliduser') {
|
||||
$('#invalidadminModal').modal({});
|
||||
} else {
|
||||
$('#cannotconvertModal').modal({});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.changeUsername = function() {
|
||||
UserService.load();
|
||||
|
||||
$scope.updatingUser = true;
|
||||
|
||||
ApiService.changeUserDetails($scope.cuser).then(function() {
|
||||
$scope.updatingUser = false;
|
||||
|
||||
// Reset the form.
|
||||
delete $scope.cuser['username'];
|
||||
|
||||
$scope.changeUsernameForm.$setPristine();
|
||||
}, function(result) {
|
||||
$scope.updatingUser = false;
|
||||
UIService.showFormError('#changeUsernameForm', result);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.changeEmail = function() {
|
||||
UIService.hidePopover('#changeEmailForm');
|
||||
|
||||
$scope.updatingUser = true;
|
||||
$scope.changeEmailSent = false;
|
||||
|
||||
ApiService.changeUserDetails($scope.cuser).then(function() {
|
||||
$scope.updatingUser = false;
|
||||
$scope.changeEmailSent = true;
|
||||
$scope.sentEmail = $scope.cuser.email;
|
||||
delete $scope.cuser['email'];
|
||||
}, function(result) {
|
||||
$scope.updatingUser = false;
|
||||
UIService.showFormError('#changeEmailForm', result);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.changePassword = function() {
|
||||
UIService.hidePopover('#changePasswordForm');
|
||||
|
||||
$scope.updatingUser = true;
|
||||
$scope.changePasswordSuccess = false;
|
||||
|
||||
ApiService.changeUserDetails($scope.cuser).then(function(resp) {
|
||||
|
||||
$scope.updatingUser = false;
|
||||
$scope.changePasswordSuccess = true;
|
||||
|
||||
// Reset the form
|
||||
delete $scope.cuser['password']
|
||||
delete $scope.cuser['repeatPassword']
|
||||
|
||||
$scope.changePasswordForm.$setPristine();
|
||||
|
||||
// Reload the user.
|
||||
UserService.load();
|
||||
}, function(result) {
|
||||
$scope.updatingUser = false;
|
||||
UIService.showFormError('#changePasswordForm', result);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.generateClientToken = function() {
|
||||
var generateToken = function(password) {
|
||||
var data = {
|
||||
'password': password
|
||||
};
|
||||
|
||||
ApiService.generateUserClientKey(data).then(function(resp) {
|
||||
$scope.generatedClientToken = resp['key'];
|
||||
$('#clientTokenModal').modal({});
|
||||
}, ApiService.errorDisplay('Could not generate token'));
|
||||
};
|
||||
|
||||
UIService.showPasswordDialog('Enter your password to generated an encrypted version:', generateToken);
|
||||
};
|
||||
|
||||
$scope.detachExternalLogin = function(kind) {
|
||||
var params = {
|
||||
'servicename': kind
|
||||
};
|
||||
|
||||
ApiService.detachExternalLogin(null, params).then(function() {
|
||||
$scope.hasGithubLogin = false;
|
||||
$scope.hasGoogleLogin = false;
|
||||
UserService.load();
|
||||
}, ApiService.errorDisplay('Count not detach service'));
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -7,7 +7,7 @@
|
|||
'newLayout': true,
|
||||
'title': 'User {{ user.username }}',
|
||||
'description': 'User {{ user.username }}'
|
||||
}, ['layout'])
|
||||
})
|
||||
}]);
|
||||
|
||||
function UserViewCtrl($scope, $routeParams, $timeout, ApiService, UserService, UIService, AvatarService) {
|
||||
|
|
|
@ -14,9 +14,7 @@ angular.module('quay').factory('AvatarService', ['Config', '$sanitize', 'md5',
|
|||
break;
|
||||
|
||||
case 'gravatar':
|
||||
// TODO(jschorr): Remove once the new layout is in place everywhere.
|
||||
var default_kind = Config.isNewLayout() ? '404' : 'identicon';
|
||||
return '//www.gravatar.com/avatar/' + hash + '?d=' + default_kind + '&size=' + size;
|
||||
return '//www.gravatar.com/avatar/' + hash + '?d=404&size=' + size;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -71,10 +71,5 @@ angular.module('quay').factory('Config', [function() {
|
|||
return value;
|
||||
};
|
||||
|
||||
config.isNewLayout = function() {
|
||||
// TODO(jschorr): Remove in the cleanup CL.
|
||||
return true;
|
||||
};
|
||||
|
||||
return config;
|
||||
}]);
|
|
@ -56,20 +56,10 @@ function($rootScope, $interval, UserService, ApiService, StringBuilderService, P
|
|||
'<br><br>Please upgrade your plan to avoid disruptions in service.',
|
||||
'page': function(metadata) {
|
||||
var organization = UserService.getOrganization(metadata['namespace']);
|
||||
|
||||
// TODO(jschorr): Remove once the new layout is in prod.
|
||||
if (Config.isNewLayout()) {
|
||||
if (organization) {
|
||||
return '/organization/' + metadata['namespace'] + '?tab=billing';
|
||||
} else {
|
||||
return '/user/' + metadata['namespace'] + '?tab=billing';
|
||||
}
|
||||
}
|
||||
|
||||
if (organization) {
|
||||
return '/organization/' + metadata['namespace'] + '/admin';
|
||||
return '/organization/' + metadata['namespace'] + '?tab=billing';
|
||||
} else {
|
||||
return '/user';
|
||||
return '/user/' + metadata['namespace'] + '?tab=billing';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
<div class="resource-view" resource="repository_build" error-message="'No matching repository build found'"></div>
|
||||
<div class="cor-container repo repo-build" ng-show="accessDenied">
|
||||
You do not have permission to view this page
|
||||
</div>
|
||||
<div class="cor-container repo repo-build repo-build-pack" ng-show="repobuild">
|
||||
<div class="header row">
|
||||
<a href="{{ '/repository/' + repo.namespace + '/' + repo.name + '/build' }}" class="back"><i class="fa fa-chevron-left"></i></a>
|
||||
<h3>
|
||||
<span class="repo-circle no-background" repo="repo"></span>
|
||||
<span class="repo-breadcrumb" repo="repo" subsection-icon="'fa-tasks'" subsection="repobuild.display_name"></span>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="downloading">
|
||||
Downloading build pack:
|
||||
<div class="progress" class="active progress-striped">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="{{ downloadProgress }}" aria-valuemin="0" aria-valuemax="100" style="{{ 'width: ' + downloadProgress + '%' }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="downloadError">
|
||||
Error: Could not download the build pack
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row" ng-show="!downloading && !downloadError && !loaded">
|
||||
Reading... <span class="quay-spinner"></span>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="loaded">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="javascript:void(0)" data-toggle="tab" data-target="#dockerfile">Dockerfile</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#tree" ng-click="initializeTree()" ng-show="tree">All Files</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<!-- Dockerfile view -->
|
||||
<div class="tab-pane active" id="dockerfile">
|
||||
<div class="dockerfile-path" ng-if="dockerFileContents">{{ dockerFilePath }}</div>
|
||||
<div class="dockerfile-view" contents="dockerFileContents" ng-if="dockerFileContents"></div>
|
||||
<div ng-if="!dockerFileContents" style="padding: 10px; margin-top: 20px;">
|
||||
No Dockerfile found in the build pack
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File tree -->
|
||||
<div class="tab-pane" id="tree">
|
||||
<div id="file-tree-container" class="tree-container" onresize="tree && drawn && tree.notifyResized()"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,10 +0,0 @@
|
|||
<div class="page-content">
|
||||
<div class="cor-title">
|
||||
<span class="cor-title-link"></span>
|
||||
<span class="cor-title-content">Experiment: New Layout</span>
|
||||
</div>
|
||||
<div class="co-main-content-panel">
|
||||
<button class="btn btn-success" ng-if="!isEnabled" ng-click="setEnabled(true)">Enable Experiment</button>
|
||||
<button class="btn btn-failure" ng-if="isEnabled" ng-click="setEnabled(false)">Disable Experiment</button>
|
||||
</div>
|
||||
</div>
|
|
@ -1,82 +0,0 @@
|
|||
<div class="resource-view" resource="image" error-message="'No image found'">
|
||||
<div class="cor-container repo repo-image-view">
|
||||
<div class="header">
|
||||
<a href="{{ '/repository/' + repo.namespace + '/' + repo.name }}" class="back"><i class="fa fa-chevron-left"></i></a>
|
||||
<h3>
|
||||
<span class="repo-circle no-background" repo="repo"></span>
|
||||
<span class="repo-breadcrumb" repo="repo" image="image.value"></span>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<!-- Comment -->
|
||||
<blockquote ng-show="image.value.comment">
|
||||
<span class="markdown-view" content="image.value.comment"></span>
|
||||
</blockquote>
|
||||
|
||||
<!-- Information -->
|
||||
<dl class="dl-normal">
|
||||
<dt>Full Image ID</dt>
|
||||
<dd>
|
||||
<div class="copy-box" value="image.value.id"></div>
|
||||
</dd>
|
||||
<dt>Created</dt>
|
||||
<dd am-time-ago="parseDate(image.value.created)"></dd>
|
||||
<dt>Compressed Image Size</dt>
|
||||
<dd><span class="context-tooltip"
|
||||
data-title="The amount of data sent between Docker and the registry when pushing/pulling"
|
||||
bs-tooltip="tooltip.title" data-container="body">{{ image.value.size | bytes }}</span>
|
||||
</dd>
|
||||
|
||||
<dt ng-show="image.value.command && image.value.command.length">Command</dt>
|
||||
<dd ng-show="image.value.command && image.value.command.length">
|
||||
<pre class="formatted-command">{{ getFormattedCommand(image.value) }}</pre>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<!-- Changes tabs -->
|
||||
<div ng-show="combinedChanges.length > 0">
|
||||
<b>File Changes:</b>
|
||||
<br>
|
||||
<br>
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="javascript:void(0)" data-toggle="tab" data-target="#filterable">Filterable View</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#tree" ng-click="initializeTree()">Tree View</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Changes tab content -->
|
||||
<div class="tab-content" ng-show="combinedChanges.length > 0">
|
||||
<!-- Filterable view -->
|
||||
<div class="tab-pane active" id="filterable">
|
||||
<div class="changes-container full-changes-container">
|
||||
<div class="change-side-controls">
|
||||
<div class="result-count">
|
||||
Showing {{(combinedChanges | filter:search | limitTo:50).length}} of {{(combinedChanges | filter:search).length}} results
|
||||
</div>
|
||||
<div class="filter-input">
|
||||
<input id="change-filter" class="form-control" placeholder="Filter Changes" type="text" ng-model="search.$">
|
||||
</div>
|
||||
</div>
|
||||
<div style="height: 28px;"></div>
|
||||
<div class="changes-list well well-sm">
|
||||
<div ng-show="(combinedChanges | filter:search | limitTo:1).length == 0">
|
||||
No matching changes
|
||||
</div>
|
||||
<div class="change" ng-repeat="change in combinedChanges | filter:search | limitTo:50">
|
||||
<i ng-class="{'added': 'fa fa-plus-square', 'removed': 'fa fa-minus-square', 'changed': 'fa fa-pencil-square'}[change.kind]"></i>
|
||||
<span data-title="{{change.file}}">
|
||||
<span style="color: #888;">
|
||||
<span ng-repeat="folder in getFolders(change.file) track by $index"><a href="javascript:void(0)" ng-click="setFolderFilter(getFolder(change.file), $index)">{{folder}}</a>/</span></span><span>{{getFilename(change.file)}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tree view -->
|
||||
<div class="tab-pane" id="tree">
|
||||
<div id="changes-tree-container" class="changes-container" onresize="tree && tree.notifyResized()"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,191 +0,0 @@
|
|||
<div class="resource-view" resource="appResource" error-message="'Application not found'">
|
||||
</div>
|
||||
|
||||
<div ng-show="application">
|
||||
<div class="cor-container manage-application">
|
||||
<!-- Header -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="auth-header">
|
||||
<span class="avatar" size="48" email="application.avatar_email" name="application.name"></span>
|
||||
<h2>{{ application.name || '(Untitled)' }}</h2>
|
||||
<h4>
|
||||
<span class="avatar" size="24" data="organization.avatar" style="vertical-align: middle; margin-right: 4px;"></span>
|
||||
<span style="vertical-align: middle"><a href="/organization/{{ organization.name }}/admin">{{ organization.name }}</a></span>
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin-top: 10px" ng-if="!application.redirect_uri">
|
||||
<div class="alert alert-warning">
|
||||
Warning: There is no OAuth Redirect setup for this application. Please enter it in the <strong>Settings</strong> tab.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="row" style="margin-top: 10px">
|
||||
<!-- Side tabs -->
|
||||
<div class="col-md-2">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li class="active"><a href="javascript:void(0)" data-toggle="tab" data-target="#settings">Settings</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#oauth">OAuth Information</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#gen-token">Generate Token</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#delete">Delete Application</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="col-md-10">
|
||||
<div class="tab-content">
|
||||
<!-- Settings tab -->
|
||||
<div id="settings" class="tab-pane active">
|
||||
<form method="put" name="applicationForm" id="applicationForm" ng-submit="updateApplication()">
|
||||
<div class="form-group nested">
|
||||
<label for="fieldAppName">Application Name</label>
|
||||
<input type="text" class="form-control" id="fieldAppName" placeholder="Application Name" required ng-model="application.name">
|
||||
<div class="description">The name of the application that is displayed to users</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group nested">
|
||||
<label for="fieldAppURI">Homepage URL</label>
|
||||
<input type="url" class="form-control" id="fieldAppURI" placeholder="Homepage URL" required ng-model="application.application_uri">
|
||||
<div class="description">The URL to which the application will link in the authorization view</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group nested">
|
||||
<label for="fieldAppDescription">Description (optional)</label>
|
||||
<input type="text" class="form-control" id="fieldAppURI" placeholder="Description" ng-model="application.description">
|
||||
<div class="description">The user friendly description of the application</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group nested">
|
||||
<label for="fieldAppAvatar">Avatar E-mail (optional)</label>
|
||||
<input type="email" class="form-control" id="fieldAppAvatar" placeholder="Avatar E-mail" ng-model="application.avatar_email">
|
||||
<div class="description">An e-mail address representing the <a href="http://docs.quay.io/glossary/avatar" target="_blank">Avatar</a> for the application. See above for the icon.</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group nested" style="margin-top: 10px; padding-top: 10px; border-top: 1px solid #eee;">
|
||||
<label for="fieldAppRedirect">Redirect/Callback URL Prefix</label>
|
||||
<input type="url" class="form-control" id="fieldAppRedirect" placeholder="OAuth Redirect URL" ng-model="application.redirect_uri" required>
|
||||
<div class="description">Allowed prefix for the application's OAuth redirection/callback URLs</div>
|
||||
</div>
|
||||
|
||||
<div class="button-bar">
|
||||
<button class="btn btn-success btn-large" type="submit" ng-disabled="applicationForm.$invalid || updating">
|
||||
Update Application
|
||||
</button>
|
||||
<span class="quay-spinner" ng-show="updating"></span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Delete tab -->
|
||||
<div id="delete" class="tab-pane">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div style="text-align: center">
|
||||
<div class="alert alert-danger">Deleting an application <b>cannot be undone</b>. Any existing users of your application will <strong>break!</strong>. Here be dragons!</div>
|
||||
<button class="btn btn-danger" ng-click="askDelete()">Delete Application</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Generate Token tab -->
|
||||
<div id="gen-token" class="tab-pane">
|
||||
<div style="margin-bottom: 20px">
|
||||
Click the button below to generate a new <a href="http://tools.ietf.org/html/rfc6749#section-1.4" target="_new">OAuth 2 Access Token</a>.
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 20px">
|
||||
<strong>Note:</strong> The generated token will act on behalf of user
|
||||
<span class="avatar" data="user.avatar" size="16" style="margin-left: 6px; margin-right: 4px;"></span>
|
||||
{{ user.username }}
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<tr ng-repeat="(scopeName, scopeInfo) in OAuthService.SCOPES">
|
||||
<td><label onclick="event.stopPropagation()"><input type="checkbox" value="scopeInfo[0]" ng-model="genScopes[scopeName]">{{ scopeInfo[3] }}</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<a class="btn btn-success"
|
||||
href="{{ Config.getUrl('/oauth/authorize?response_type=token&client_id=' + application.client_id + '&scope=' + getScopes(genScopes).join(',') + '&redirect_uri=display') }}"
|
||||
ng-disabled="!getScopes(genScopes).length" target="_blank">
|
||||
Generate Access Token
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- OAuth tab -->
|
||||
<div id="oauth" class="tab-pane">
|
||||
<table style="margin-top: 20px;">
|
||||
<thead>
|
||||
<th style="width: 150px"></th>
|
||||
<th style="width: 250px"></th>
|
||||
<th></th>
|
||||
</thead>
|
||||
<tr>
|
||||
<td>Client ID:</td>
|
||||
<td style="width: 250px">
|
||||
<div class="copy-box" hovering-message="true" value="application.client_id"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Client Secret: <i class="fa fa-lock fa-lg" data-title="Keep this secret!" bs-tooltip style="margin-left: 10px"></i></td>
|
||||
<td>
|
||||
{{ application.client_secret }}
|
||||
</td>
|
||||
<td style="padding-left: 10px">
|
||||
<button class="btn btn-primary" ng-click="askResetClientSecret()">Reset Client Secret</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="resetSecretModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Reset Client Secret?</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-info">
|
||||
Note that resetting the Client Secret for this application will <strong>not</strong> invalidate any user tokens.
|
||||
</div>
|
||||
<div>Are you sure you want to reset your Client Secret? Any existing users of this Secret <strong>will break!</strong></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" ng-click="resetClientSecret()">Yes, I'm sure</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="deleteAppModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Delete Application?</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Are you <b>absolutely, positively</b> sure you would like to delete this application? This <b>cannot be undone</b>.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" ng-click="deleteApplication()">Delete Application</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
|
@ -1,94 +0,0 @@
|
|||
<div class="loading" ng-show="creating">
|
||||
<div class="quay-spinner"></div>
|
||||
</div>
|
||||
|
||||
<div class="cor-container create-org" ng-show="!creating">
|
||||
|
||||
<div class="row header-row">
|
||||
<div class="col-md-12">
|
||||
<h2>Create Organization</h2>
|
||||
|
||||
<div class="steps-container" ng-show="false">
|
||||
<ul class="steps">
|
||||
<li class="step" ng-class="!user || user.anonymous ? 'active' : ''">
|
||||
<i class="fa fa-sign-in"></i>
|
||||
<span class="title">Login with an account</span>
|
||||
</li>
|
||||
<li class="step" ng-class="!user.anonymous && !created ? 'active' : ''">
|
||||
<i class="fa fa-gear"></i>
|
||||
<span class="title">Setup your organization</span>
|
||||
</li>
|
||||
<li class="step" ng-class="!user.anonymous && created ? 'active' : ''">
|
||||
<i class="fa fa-group"></i>
|
||||
<span class="title">Create teams</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 1 -->
|
||||
<div class="row" ng-show="!user || user.anonymous">
|
||||
<div class="col-md-10 col-md-offset-1 page-description">
|
||||
In order to create a new organization, <b>you must first be signed in</b> as the
|
||||
user that <b>will become an admin</b> for the organization.
|
||||
</div>
|
||||
<div class="col-sm-6 col-sm-offset-3">
|
||||
<div class="step-container" >
|
||||
<div class="user-setup" redirect-url="'/organizations/new'" sign-in-started="signinStarted()"
|
||||
signed-in="signedIn()"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2 -->
|
||||
<div class="row" ng-show="user && !user.anonymous && !created">
|
||||
<div class="col-md-12">
|
||||
<div class="step-container">
|
||||
<h3>Setup the new organization</h3>
|
||||
|
||||
<form method="post" name="newOrgForm" id="newOrgForm" ng-submit="createNewOrg()">
|
||||
<div class="form-group nested">
|
||||
<label for="orgName">Organization Name</label>
|
||||
<input id="orgName" name="orgName" type="text" class="form-control" placeholder="Organization Name"
|
||||
ng-model="org.name" required autofocus data-trigger="manual" data-content="{{ createError }}"
|
||||
data-placement="bottom" data-container="body" ng-pattern="/^[a-z0-9_]{4,30}$/">
|
||||
<span class="description">This will also be the namespace for your repositories. Must be alphanumeric and all lowercase.</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group nested">
|
||||
<label for="orgName">Organization Email</label>
|
||||
<input id="orgEmail" name="orgEmail" type="email" class="form-control" placeholder="Organization Email"
|
||||
ng-model="org.email" required>
|
||||
<span class="description">This address must be different from your account's email.</span>
|
||||
</div>
|
||||
|
||||
<!-- Plans Table -->
|
||||
<div class="form-group nested plan-group" quay-require="['BILLING']">
|
||||
<strong>Choose your organization's plan</strong>
|
||||
<div class="plans-table" plans="plans" current-plan="holder.currentPlan"></div>
|
||||
</div>
|
||||
|
||||
<div class="button-bar">
|
||||
<button class="btn btn-large btn-success" type="submit"
|
||||
ng-disabled="newOrgForm.$invalid || (Features.BILLING && !holder.currentPlan)"
|
||||
analytics-on analytics-event="create_organization">
|
||||
Create Organization
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 3 -->
|
||||
<div class="row" ng-show="user && !user.anonymous && created">
|
||||
<div class="col-md-12">
|
||||
<div class="step-container">
|
||||
<h3>Organization Created</h3>
|
||||
<h4><a href="/organization/{{ org.name }}">Manage Teams Now</a></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,237 +0,0 @@
|
|||
<div class="cor-container" ng-show="user.anonymous">
|
||||
<div class="col-sm-6 col-sm-offset-3">
|
||||
<div class="user-setup" redirect-url="'/new/'"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cor-container" ng-show="!user.anonymous && building">
|
||||
<div class="quay-spinner"></div>
|
||||
</div>
|
||||
|
||||
<div class="cor-container" ng-show="!user.anonymous && creating">
|
||||
<div class="quay-spinner"></div>
|
||||
</div>
|
||||
|
||||
<div class="cor-container new-repo" ng-show="!user.anonymous && !creating && !building">
|
||||
<form method="post" name="newRepoForm" id="newRepoForm" ng-submit="createNewRepo()">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="section">
|
||||
<div class="new-header">
|
||||
<span style="color: #444;">
|
||||
<span class="namespace-selector" user="user" namespace="repo.namespace" require-create="true"></span>
|
||||
<span style="color: #ccc">/</span>
|
||||
<span class="name-container">
|
||||
<input id="repoName" name="repoName" type="text" class="form-control" placeholder="Repository Name" ng-model="repo.name"
|
||||
required autofocus data-trigger="manual" data-content="{{ createError }}" data-placement="right" ng-pattern="/^[.a-z0-9_-]+$/">
|
||||
</span>
|
||||
<span class="alert alert-warning" ng-show="!newRepoForm.repoName.$error.required && !newRepoForm.repoName.$valid" style="margin-left: 10px;">
|
||||
Repository names must match [a-z0-9_-]+
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Repository Description</div>
|
||||
<br>
|
||||
<div class="description markdown-input" content="repo.description" can-write="true"
|
||||
field-title="'repository description'"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Private/public -->
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="section-title">Repository Visibility</div>
|
||||
<div class="section">
|
||||
<div class="repo-option">
|
||||
<input type="radio" id="publicrepo" name="publicorprivate" ng-model="repo.is_public" value="1">
|
||||
<i class="fa fa-unlock fa-large" data-title="Public Repository"></i>
|
||||
|
||||
<div class="option-description">
|
||||
<label for="publicrepo"><strong>Public</strong></label>
|
||||
<span class="description-text">Anyone can see and pull from this repository. You choose who can push.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="repo-option">
|
||||
<input type="radio" id="privaterepo" name="publicorprivate" ng-model="repo.is_public" value="0">
|
||||
<i class="fa fa-lock fa-large" data-title="Private Repository"></i>
|
||||
|
||||
<div class="option-description">
|
||||
<label for="privaterepo"><strong>Private</strong></label>
|
||||
<span class="description-text">You choose who can see, pull and push from/to this repository.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Payment -->
|
||||
<div class="required-plan" ng-show="repo.is_public == '0' && planRequired && planRequired.title">
|
||||
<div class="alert alert-warning">
|
||||
In order to make this repository private
|
||||
<span ng-if="isUserNamespace">under your personal namespace</span>
|
||||
<span ng-if="!isUserNamespace">under the organization <b>{{ repo.namespace }}</b></span>, you will need to upgrade your plan to
|
||||
<b style="border-bottom: 1px dotted black;" data-html="true"
|
||||
data-title="{{ '<b>' + planRequired.title + '</b><br>' + planRequired.privateRepos + ' private repositories' }}" bs-tooltip>
|
||||
{{ planRequired.title }}
|
||||
</b>.
|
||||
This will cost $<span>{{ planRequired.price / 100 }}</span>/month.
|
||||
</div>
|
||||
<a class="btn btn-primary" ng-click="upgradePlan()" ng-show="!planChanging">Upgrade now</a>
|
||||
<span ng-if="isUserNamespace && user.organizations.length == 1">or did you mean to create this repository
|
||||
under <a href="javascript:void(0)" ng-click="changeNamespace(user.organizations[0].name)"><b>{{ user.organizations[0].name }}</b></a>?</span>
|
||||
<div class="quay-spinner" ng-show="planChanging"></div>
|
||||
</div>
|
||||
|
||||
<div class="quay-spinner" ng-show="repo.is_public == '0' && checkingPlan"></div>
|
||||
|
||||
<div class="required-plan" ng-show="repo.is_public == '0' && planRequired && !isUserNamespace && !planRequired.title">
|
||||
<div class="alert alert-warning">
|
||||
This organization has reached its private repository limit. Please contact your administrator.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="Features.BUILD_SUPPORT">
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="section">
|
||||
<div class="section-title">Initialize repository</div>
|
||||
|
||||
<div style="padding-top: 10px;">
|
||||
<!-- Empty -->
|
||||
<div class="repo-option">
|
||||
<input type="radio" id="initEmpty" name="initialize" ng-model="repo.initialize" value="">
|
||||
<i class="fa fa-hdd-o fa-lg" style="padding: 6px; padding-left: 8px; padding-right: 6px;"></i>
|
||||
<label for="initEmpty" style="color: #aaa;">(Empty repository)</label>
|
||||
</div>
|
||||
|
||||
<!-- Dockerfile -->
|
||||
<div class="repo-option">
|
||||
<input type="radio" id="initDockerfile" name="initialize" ng-model="repo.initialize" value="dockerfile">
|
||||
<i class="fa fa-file fa-lg" style="padding: 6px; padding-left: 10px; padding-right: 8px;"></i>
|
||||
<label for="initDockerfile">Initialize from a <b>Dockerfile</b></label>
|
||||
</div>
|
||||
|
||||
<!-- Zip/TarGz file -->
|
||||
<div class="repo-option">
|
||||
<input type="radio" id="initZipfile" name="initialize" ng-model="repo.initialize" value="zipfile">
|
||||
<i class="fa fa-archive fa-lg" style="padding: 6px; padding-left: 10px; padding-right: 8px;"></i>
|
||||
<label for="initZipfile">Initialize from a <b>Dockerfile</b> inside a <code>.zip</code> or <code>.tar.gz</code> archive</label>
|
||||
</div>
|
||||
|
||||
<!-- Github -->
|
||||
<div class="repo-option" ng-show="Features.GITHUB_BUILD">
|
||||
<input type="radio" id="initGithub" name="initialize" ng-model="repo.initialize" value="github">
|
||||
<i class="fa fa-github fa-lg" style="padding: 6px; padding-left: 10px; padding-right: 12px;"></i>
|
||||
<label for="initGithub">Link to a GitHub Repository</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="repo.initialize == 'dockerfile' || repo.initialize == 'zipfile'">
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="section">
|
||||
<div class="section-title">Upload <span ng-if="repo.initialize == 'dockerfile'">Dockerfile</span><span ng-if="repo.initialize == 'zipfile'">Archive</span></div>
|
||||
<div style="padding-top: 20px;">
|
||||
<div class="initialize-repo">
|
||||
<div class="dockerfile-build-form" repository="createdForBuild || repo" upload-failed="handleBuildFailed(message)"
|
||||
build-started="handleBuildStarted()" build-failed="handleBuildFailed(message)" start-now="createdForBuild"
|
||||
is-ready="hasDockerfile" uploading="uploading" building="building"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="repo.initialize == 'github'">
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-info">
|
||||
You will be redirected to authorize via GitHub once the repository has been created
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-12">
|
||||
<button class="btn btn-large btn-success" type="submit"
|
||||
ng-disabled="uploading || building || newRepoForm.$invalid || (repo.is_public == '0' && (planRequired || checkingPlan)) || ((repo.initialize == 'dockerfile' || repo.initialize == 'zipfile') && !hasDockerfile)">
|
||||
<i class="fa fa-large" ng-class="repo.is_public == '1' ? 'fa-unlock' : 'fa-lock'" style="margin-right: 4px"></i>
|
||||
Create {{ repo.is_public == '1' ? 'Public' : 'Private' }} Repository
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Modal edit for the description -->
|
||||
<div class="modal fade" id="editModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Edit Repository Description</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="wmd-panel">
|
||||
<div id="wmd-button-bar-description"></div>
|
||||
<textarea class="wmd-input" id="wmd-input-description" placeholder="Enter description">{{ repo.description }}</textarea>
|
||||
</div>
|
||||
|
||||
<div id="wmd-preview-description" class="wmd-panel wmd-preview"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary" ng-click="saveDescription()">Save changes</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="cannotcreateModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Cannot create repository</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
The repository could not be created.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="couldnotsubscribeModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Cannot upgrade plan</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Your current plan could not be upgraded. Please try again.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
|
@ -1,86 +0,0 @@
|
|||
<div class="resource-view" resource="orgResource" error-message="'No matching organization found'">
|
||||
<div class="org-view cor-container">
|
||||
<div class="organization-header" organization="organization">
|
||||
<div class="header-buttons" ng-show="organization.is_admin">
|
||||
|
||||
<span class="popup-input-button" pattern="TEAM_PATTERN" placeholder="'Team Name'"
|
||||
submitted="createTeam(value)">
|
||||
<i class="fa fa-group"></i> Create Team
|
||||
</span>
|
||||
|
||||
<a class="btn btn-default" href="/organization/{{ organization.name }}/admin"><i class="fa fa-gear"></i> Settings</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row hidden-xs">
|
||||
<div class="col-md-4 col-md-offset-8 col-sm-5 col-sm-offset-7 header-col" ng-show="organization.is_admin">
|
||||
Team Permissions
|
||||
<i class="info-icon fa fa-info-circle" data-placement="bottom" data-original-title="" data-title=""
|
||||
data-content="Global permissions for the team and its members<br><br><dl><dt>Member</dt><dd>Permissions are assigned on a per repository basis</dd><dt>Creator</dt><dd>A team can create its own repositories</dd><dt>Admin</dt><dd>A team has full control of the organization</dd></dl>"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="team-listing" ng-repeat="(name, team) in organization.teams">
|
||||
<div id="team-{{name}}" class="row">
|
||||
<div class="col-sm-7 col-md-8">
|
||||
<div class="team-title">
|
||||
<i class="fa fa-group"></i>
|
||||
<span ng-show="team.can_view">
|
||||
<a href="/organization/{{ organization.name }}/teams/{{ team.name }}">{{ team.name }}</a>
|
||||
</span>
|
||||
<span ng-show="!team.can_view">
|
||||
{{ team.name }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="team-description markdown-view" content="team.description" first-line-only="true"></div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-5 col-md-4 control-col" ng-show="organization.is_admin">
|
||||
<span class="role-group" current-role="team.role" role-changed="setRole(role, team.name)"
|
||||
roles="teamRoles"></span>
|
||||
<button class="btn btn-sm btn-danger" ng-click="askDeleteTeam(team.name)">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="cannotChangeTeamModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Cannot change team</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<span ng-show="!roleError">You do not have permission to change properties on teams.</span>
|
||||
<span ng-show="roleError">{{ roleError }}</span>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="confirmdeleteModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Delete Team?</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Are you sure you would like to delete this team? This <b>cannot be undone</b>.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" ng-click="deleteTeam()">Delete Team</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
|
@ -1,74 +0,0 @@
|
|||
<div class="cor-container">
|
||||
<div class="repo-list" ng-show="!user.anonymous">
|
||||
<div ng-class="user.organizations.length ? 'section-header' : ''">
|
||||
<div class="button-bar-right">
|
||||
<a href="/new/">
|
||||
<button class="btn btn-success">
|
||||
<i class="fa fa-upload user-tool" data-title="Create new repository"></i>
|
||||
Create Repository
|
||||
</button>
|
||||
</a>
|
||||
|
||||
<a href="/organization/{{ namespace }}" ng-show="namespace != user.username">
|
||||
<button class="btn btn-default">
|
||||
<i class="fa fa-group user-tool"></i>
|
||||
View Organization
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<span class="namespace-selector" user="user" namespace="namespace" ng-show="user.organizations"></span>
|
||||
</div>
|
||||
|
||||
<h3 ng-show="namespace == user.username">Your Repositories</h3>
|
||||
<h3 ng-show="namespace != user.username">Repositories</h3>
|
||||
|
||||
<div class="resource-view" resource="user_repositories">
|
||||
<!-- User/Org has repositories -->
|
||||
<div ng-show="user_repositories.value.length > 0">
|
||||
<div class="repo-listing" ng-repeat="repository in user_repositories.value">
|
||||
<span class="repo-circle no-background" repo="repository"></span>
|
||||
<a ng-href="/repository/{{repository.namespace}}/{{ repository.name }}"
|
||||
data-repo="{{repository.namespace}}/{{ repository.name }}">
|
||||
{{repository.namespace}}/{{repository.name}}
|
||||
</a>
|
||||
<div class="description markdown-view" content="repository.description" first-line-only="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- User/Org has no repositories -->
|
||||
<div ng-show="user_repositories.value.length == 0" style="padding:20px;">
|
||||
<div class="alert alert-info">
|
||||
<h4 ng-show="namespace == user.username">You don't have any repositories yet!</h4>
|
||||
<h4 ng-show="namespace != user.username">This organization doesn't have any repositories, or you have not been provided access.</h4>
|
||||
<a href="http://docs.quay.io/solution/getting-started.html"><b>Click here</b> to learn how to create a repository</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="repo-list">
|
||||
<h3>Top Public Repositories</h3>
|
||||
<div class="resource-view" resource="public_repositories">
|
||||
<div class="repo-listing" ng-repeat="repository in public_repositories.value">
|
||||
<span class="repo-circle no-background" repo="repository"></span>
|
||||
<a ng-href="/repository/{{repository.namespace}}/{{ repository.name }}"
|
||||
data-repo="{{repository.namespace}}/{{ repository.name }}">
|
||||
{{repository.namespace}}/{{repository.name}}
|
||||
</a>
|
||||
<div class="description markdown-view" content="repository.description" first-line-only="true"></div>
|
||||
</div>
|
||||
<div class="page-controls">
|
||||
<button class="btn btn-default" data-title="Previous Page" bs-tooltip="title" ng-show="page > 1"
|
||||
ng-click="movePublicPage(-1)">
|
||||
<i class="fa fa-chevron-left"></i>
|
||||
</button>
|
||||
<button class="btn btn-default" data-title="Next Page" bs-tooltip="title" ng-show="page < publicPageCount"
|
||||
ng-click="movePublicPage(1)">
|
||||
<i class="fa fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,425 +0,0 @@
|
|||
<div id="tagContextMenu" class="dropdown clearfix" tabindex="-1">
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu">
|
||||
<li><a tabindex="-1" href="javascript:void(0)" ng-click="askDeleteTag(currentMenuTag)">Delete Tag</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="resource-view" resource="repository" error-message="'No Repository Found'">
|
||||
<div class="container-fluid repo repo-view">
|
||||
<!-- Repo Header -->
|
||||
<div class="header">
|
||||
<h3>
|
||||
<span class="repo-circle no-background" repo="repo"></span>
|
||||
<span class="repo-breadcrumb" repo="repo"></span>
|
||||
</h3>
|
||||
|
||||
<div class="repo-controls">
|
||||
<!-- Builds -->
|
||||
<div class="dropdown" data-placement="top" style="display: inline-block"
|
||||
bs-tooltip=""
|
||||
data-title="{{ runningBuilds.length ? 'Dockerfile Builds Running: ' + (runningBuilds.length) : 'Dockerfile Build' }}"
|
||||
quay-show="Features.BUILD_SUPPORT && (repo.can_write || buildHistory.length)">
|
||||
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-tasks fa-lg"></i>
|
||||
<span class="count" ng-class="runningBuilds.length ? 'visible' : ''"><span>{{ runningBuilds.length ? runningBuilds.length : '' }}</span></span>
|
||||
<b class="caret"></b>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li ng-show="repo.can_write"><a href="{{ '/repository/' + repo.namespace + '/' + repo.name + '/build' }}">
|
||||
<i class="fa fa-tasks"></i>Dockerfile Build History</a>
|
||||
</li>
|
||||
<li ng-show="repo.can_write">
|
||||
<a href="javascript:void(0)" ng-click="showNewBuildDialog()">
|
||||
<i class="fa fa-plus" style="margin-left: 1px; margin-right: 8px;"></i>New Dockerfile Build
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="repo.can_admin">
|
||||
<a href="/repository/{{ repo.namespace }}/{{ repo.name }}/admin?tab=trigger">
|
||||
<i class="fa fa-bolt" style="margin-left: 3px; margin-right: 10px;"></i>Build Triggers
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation" class="divider" ng-show="buildHistory && repo.can_write"></li>
|
||||
<li role="presentation" class="dropdown-header" ng-show="buildHistory.length">Recent Builds</li>
|
||||
<li ng-repeat="buildInfo in buildHistory">
|
||||
<div class="build-info" ng-class="repo.can_write ? 'clickable' : ''" ng-click="repo.can_write && showBuild(buildInfo)">
|
||||
<span class="build-status" build="buildInfo"></span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Admin -->
|
||||
<a id="admin-cog" href="{{ '/repository/' + repo.namespace + '/' + repo.name + '/admin' }}"
|
||||
ng-show="repo.can_admin">
|
||||
<button class="btn btn-default" data-title="Repository Settings" bs-tooltip="tooltip" data-placement="top">
|
||||
<i class="fa fa-cog fa-lg"></i></button></a>
|
||||
|
||||
<!-- Pull Command -->
|
||||
<span class="pull-command visible-md-inline">
|
||||
<div class="pull-container" ng-show="currentPullCommand">
|
||||
<button class="pull-selector dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa" ng-class="currentPullCommand.icon"></i>
|
||||
{{ currentPullCommand.shortTitle }}
|
||||
<b class="caret"></b>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li ng-repeat="pullCommand in pullCommands">
|
||||
<a href="javascript:void(0)" ng-click="setCurrentPullCommand(pullCommand)"><i class="fa" ng-class="pullCommand.icon"></i>
|
||||
{{ pullCommand.title }}
|
||||
<sup ng-if="pullCommand.experimental">Experimental</sup>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="copy-box" hovering-message="true" value="currentPullCommand.command"></div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
<div class="description markdown-input" content="repo.description" can-write="repo.can_write"
|
||||
content-changed="updateForDescription" field-title="'repository description'"></div>
|
||||
|
||||
<!-- Empty messages -->
|
||||
<div ng-if="!currentTag.image_id && !currentImage">
|
||||
<!-- !building && !pushing -->
|
||||
<div class="repo-content" ng-show="!repo.is_building && !isPushing(images)">
|
||||
<div class="empty-message">
|
||||
This repository is empty
|
||||
</div>
|
||||
|
||||
<div class="empty-description" ng-show="repo.can_write">
|
||||
<div class="panel-default">
|
||||
<div class="panel-heading">How to push a new image to this repository:</div>
|
||||
<div class="panel-body">
|
||||
First login to the registry (if you have not done so already):
|
||||
<pre class="command">sudo docker login {{ Config.getDomain() }}</pre>
|
||||
|
||||
Tag an image to this repository:
|
||||
<pre class="command">sudo docker tag <i>0u123imageidgoeshere</i> {{ Config.getDomain() }}/{{repo.namespace}}/{{repo.name}}</pre>
|
||||
|
||||
Push the image to this repository:
|
||||
<pre class="command">sudo docker push {{ Config.getDomain() }}/{{repo.namespace}}/{{repo.name}}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- building -->
|
||||
<div class="repo-content" ng-show="repo.is_building">
|
||||
<div class="empty-message">
|
||||
A build is currently processing. If this takes longer than an hour, please <a href="/contact">contact us</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- pushing -->
|
||||
<div class="repo-content" ng-show="!repo.is_building && isPushing(images)">
|
||||
<div class="empty-message">
|
||||
A push to this repository is in progress.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content view -->
|
||||
<div class="repo-content" ng-show="currentTag.image_id || currentImage">
|
||||
<!-- Image History -->
|
||||
<div id="image-history">
|
||||
<div class="row">
|
||||
<!-- Tree View container -->
|
||||
<div class="col-md-8">
|
||||
<div class="panel panel-default">
|
||||
<!-- Image history tree -->
|
||||
<div class="resource-view" resource="imageHistory">
|
||||
<div id="image-history-container" onresize="tree.notifyResized()"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Side Panel -->
|
||||
<div class="col-md-4">
|
||||
<div id="side-panel" class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<!-- Dropdown -->
|
||||
<div id="side-panel-dropdown" class="tag-dropdown dropdown" data-placement="top">
|
||||
<i class="fa fa-tag current-context-icon" ng-show="currentTag"></i>
|
||||
<i class="fa fa-archive current-context-icon" ng-show="!currentTag"></i>
|
||||
<a href="javascript:void(0)" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="current-context">
|
||||
{{currentTag ? currentTag.name : currentImage.id.substr(0, 12)}}
|
||||
</span>
|
||||
<b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li ng-repeat="tag in repo.tags">
|
||||
<a href="javascript:void(0)" ng-click="setTag(tag.name, true)">
|
||||
<i class="fa fa-tag"></i>{{tag.name}}
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li ng-repeat="image in imageHistory.value">
|
||||
<a href="javascript:void(0)" ng-click="setImage(image.id, true)">
|
||||
{{image.id.substr(0, 12)}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<span class="right-tag-controls">
|
||||
<i class="fa fa-tag" data-title="Tags" bs-tooltip="title">
|
||||
<span class="tag-count">{{getTagCount(repo)}}</span>
|
||||
</i>
|
||||
<i class="fa fa-archive" data-title="Images" bs-tooltip="title">
|
||||
<span class="tag-count">{{imageHistory.value.length}}</span>
|
||||
</i>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<!-- Current Tag -->
|
||||
<div id="current-tag" ng-show="currentTag">
|
||||
<dl class="dl-normal">
|
||||
<dt>Last Modified</dt>
|
||||
<dd ng-if="!findImageForTag(currentTag, images)">
|
||||
<span class="quay-spinner"></span>
|
||||
</dd>
|
||||
|
||||
<dd am-time-ago="parseDate(findImageForTag(currentTag, images).created)"
|
||||
ng-if="findImageForTag(currentTag, images)">
|
||||
</dd>
|
||||
|
||||
<dt>Total Compressed Size</dt>
|
||||
<dd><span class="context-tooltip"
|
||||
data-title="The amount of data sent between Docker and the registry when pushing/pulling"
|
||||
bs-tooltip="tooltip.title" data-container="body">{{ getTotalSize(currentTag) | bytes }}</span>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<div class="tag-image-sizes">
|
||||
<div class="tag-image-size" ng-repeat="image in getImagesForTagBySize(currentTag) | limitTo: 10">
|
||||
<span class="size-limiter">
|
||||
<span class="size-bar" style="{{ 'width:' + (image.size / getTotalSize(currentTag)) * 100 + '%' }}"
|
||||
bs-tooltip="" data-title="{{ image.size | bytes }}"></span>
|
||||
</span>
|
||||
<span class="size-title">
|
||||
<a class="image-size-link" href="javascript:void(0)" ng-click="setImage(image.id, true)"
|
||||
data-image="{{ image.id.substr(0, 12) }}">
|
||||
{{ image.id.substr(0, 12) }}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-bar" ng-show="repo.can_admin">
|
||||
<button class="btn btn-default" ng-click="askDeleteTag(currentTag.name)">
|
||||
Delete Tag
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current Image -->
|
||||
<div id="current-image" ng-show="currentImage && !currentTag">
|
||||
<div class="image-comment" ng-if="currentImage.comment">
|
||||
<blockquote style="margin-top: 10px;">
|
||||
<span class="markdown-view" content="currentImage.comment"></span>
|
||||
</blockquote>
|
||||
</div>
|
||||
|
||||
<div class="image-section">
|
||||
<i class="fa fa-code section-icon" bs-tooltip="tooltip.title" data-title="Full Image ID"></i>
|
||||
<span class="section-info">
|
||||
<a class="image-anchor" href="{{'/repository/' + repo.namespace + '/' + repo.name + '/image/' + currentImage.id}}">{{ currentImage.id }}</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="image-section">
|
||||
<i class="fa fa-tag section-icon" bs-tooltip="tooltip.title" data-title="Current Tags"></i>
|
||||
<span class="section-info section-info-with-dropdown">
|
||||
<a class="label tag label-default" ng-repeat="tag in currentImage.tags"
|
||||
href="/repository/{{ repo.namespace }}/{{ repo.name }}?tag={{ tag }}">
|
||||
{{ tag }}
|
||||
</a>
|
||||
<span style="color: #ccc;" ng-if="!currentImage.tags.length">(No Tags)</span>
|
||||
|
||||
|
||||
<div class="dropdown" data-placement="top" ng-if="repo.can_write || currentImage.tags">
|
||||
<a href="javascript:void(0)" class="dropdown-button" data-toggle="dropdown" bs-tooltip="tooltip.title" data-title="Manage Tags"
|
||||
data-container="body">
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right">
|
||||
<li ng-repeat="tag in currentImage.tags">
|
||||
<a href="javascript:void(0)" ng-click="setTag(tag, true)">
|
||||
<i class="fa fa-tag"></i>{{tag}}
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider" role="presentation" ng-if="repo.can_write && currentImage.tags"></li>
|
||||
<li>
|
||||
<a href="javascript:void(0)" ng-click="showAddTag(currentImage)" ng-if="repo.can_write">
|
||||
<i class="fa fa-plus"></i>Add New Tag
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="image-section" ng-if="currentImage.command && currentImage.command.length">
|
||||
<i class="fa fa-terminal section-icon" bs-tooltip="tooltip.title" data-title="Image Command"></i>
|
||||
<span class="section-info">
|
||||
<span class="formatted-command trimmed"
|
||||
data-html="true"
|
||||
bs-tooltip="" data-title="{{ getTooltipCommand(currentImage) }}"
|
||||
data-placement="top">{{ getFormattedCommand(currentImage) }}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="image-section">
|
||||
<i class="fa fa-calendar section-icon" bs-tooltip="tooltip.title" data-title="Created"></i>
|
||||
<span class="section-info">
|
||||
<dd am-time-ago="parseDate(currentImage.created)"></dd>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="image-section">
|
||||
<i class="fa fa-cloud-upload section-icon" bs-tooltip="tooltip.title"
|
||||
data-title="The amount of data sent between Docker and the registry when pushing/pulling"></i>
|
||||
<span class="section-info">{{ currentImage.size | bytes }}</span>
|
||||
</div>
|
||||
|
||||
<div class="image-section">
|
||||
<i class="fa fa-map-marker section-icon" bs-tooltip="tooltip.title"
|
||||
data-title="The geographic region(s) in which this image data is located"></i>
|
||||
<span class="section-info">
|
||||
<span class="location-view" location="location" ng-repeat="location in currentImage.locations"></span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Image changes loading -->
|
||||
<div class="resource-view" resource="currentImageChangeResource">
|
||||
<div class="changes-container small-changes-container section-info"
|
||||
ng-show="currentImageChanges.changed.length || currentImageChanges.added.length || currentImageChanges.removed.length">
|
||||
<div class="changes-count-container image-section">
|
||||
<i class="fa fa-code-fork section-icon" bs-tooltip="tooltip.title" data-title="File Changes"></i>
|
||||
<div style="float: right; display: inline-block">
|
||||
<span class="change-count added" ng-show="currentImageChanges.added.length > 0" data-title="Files Added"
|
||||
bs-tooltip="tooltip.title" data-placement="top" data-container="body">
|
||||
<i class="fa fa-plus-square"></i>
|
||||
<b>{{currentImageChanges.added.length}}</b>
|
||||
</span>
|
||||
<span class="change-count removed" ng-show="currentImageChanges.removed.length > 0" data-title="Files Removed"
|
||||
bs-tooltip="tooltip.title" data-placement="top" data-container="body">
|
||||
<i class="fa fa-minus-square"></i>
|
||||
<b>{{currentImageChanges.removed.length}}</b>
|
||||
</span>
|
||||
<span class="change-count changed" ng-show="currentImageChanges.changed.length > 0" data-title="Files Changed"
|
||||
bs-tooltip="tooltip.title" data-placement="top" data-container="body">
|
||||
<i class="fa fa-pencil-square"></i>
|
||||
<b>{{currentImageChanges.changed.length}}</b>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapseChanges" style="padding-top: 24px;">
|
||||
<div class="well well-sm">
|
||||
<div class="change added" ng-repeat="file in currentImageChanges.added | limitTo:2">
|
||||
<i class="fa fa-plus-square"></i>
|
||||
<span title="{{file}}">{{file}}</span>
|
||||
</div>
|
||||
<div class="change removed" ng-repeat="file in currentImageChanges.removed | limitTo:2">
|
||||
<i class="fa fa-minus-square"></i>
|
||||
<span title="{{file}}">{{file}}</span>
|
||||
</div>
|
||||
<div class="change changed" ng-repeat="file in currentImageChanges.changed | limitTo:2">
|
||||
<i class="fa fa-pencil-square"></i>
|
||||
<span title="{{file}}">{{file}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="more-changes" ng-show="getMoreCount(currentImageChanges) > 0">
|
||||
<a href="{{'/repository/' + repo.namespace + '/' + repo.name + '/image/' + currentImage.id}}">
|
||||
And {{getMoreCount(currentImageChanges)}} more...
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dockerfile-build-dialog" show-now="buildDialogShowCounter" repository="repo"
|
||||
build-started="handleBuildStarted(build)">
|
||||
</div>
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="addTagModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" ng-show="!creatingTag">×</button>
|
||||
<h4 class="modal-title">{{ isAnotherImageTag(toTagImage, tagToCreate) ? 'Move' : 'Add' }} Tag to Image {{ toTagImage.id.substr(0, 12) }}</h4>
|
||||
</div>
|
||||
<form name="addTagForm" ng-submit="createOrMoveTag(toTagImage, tagToCreate, addTagForm.$invalid); addTagForm.$setPristine(); tagToCreate=''">
|
||||
<div class="modal-body">
|
||||
<input type="text" class="form-control" id="tagName" placeholder="Enter tag name"
|
||||
ng-model="tagToCreate" ng-pattern="/^([a-z0-9_\.-]){3,30}$/" required
|
||||
ng-disabled="creatingTag" autofocus>
|
||||
<div style="margin: 10px; margin-top: 20px;" ng-show="isOwnedTag(toTagImage, tagToCreate)">
|
||||
Note: <span class="label tag label-default">{{ tagToCreate }}</span> is already applied to this image.
|
||||
</div>
|
||||
<div style="margin: 10px; margin-top: 20px;" ng-show="isAnotherImageTag(toTagImage, tagToCreate)">
|
||||
Note: <span class="label tag label-default">{{ tagToCreate }}</span> is already applied to another image. This will <b>move</b> the tag.
|
||||
</div>
|
||||
<div class="tag-specific-images-view" tag="tagToCreate" repository="repo" images="images" image-cutoff="toTagImage"
|
||||
style="margin: 10px; margin-top: 20px; margin-bottom: -10px;" ng-show="isAnotherImageTag(toTagImage, tagToCreate)">
|
||||
This will also delete any unattach images and delete the following images:
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary"
|
||||
ng-disabled="!tagToCreate || addTagForm.$invalid || isOwnedTag(toTagImage, tagToCreate)"
|
||||
ng-class="isAnotherImageTag(toTagImage, tagToCreate) ? 'btn-warning' : 'btn-primary'" ng-show="!creatingTag">
|
||||
{{ isAnotherImageTag(toTagImage, tagToCreate) ? 'Move Tag' : 'Create Tag' }}
|
||||
</button>
|
||||
<button class="btn btn-default" data-dismiss="modal" ng-show="!creatingTag">Cancel</button>
|
||||
<div class="quay-spinner" ng-show="creatingTag"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="confirmdeleteTagModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Delete tag
|
||||
<span class="label tag" ng-class="tagToDelete == currentTag.name ? 'label-success' : 'label-default'">
|
||||
{{ tagToDelete }}
|
||||
</span>?
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body" ng-show="deletingTag">
|
||||
<div class="quay-spinner"></div>
|
||||
</div>
|
||||
<div class="modal-body" ng-show="!deletingTag">
|
||||
Are you sure you want to delete tag
|
||||
<span class="label tag" ng-class="tagToDelete == currentTag.name ? 'label-success' : 'label-default'">
|
||||
{{ tagToDelete }}
|
||||
</span>?
|
||||
|
||||
<div class="tag-specific-images-view" tag="tagToDelete" repository="repo" images="images" style="margin-top: 20px">
|
||||
The following images and any other images not referenced by a tag will be deleted:
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer" ng-show="!deletingTag">
|
||||
<button type="button" class="btn btn-primary" ng-click="deleteTag(tagToDelete)">Delete Tag</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
<div class="resource-view" resource="orgResource" error-message="'No matching organization'">
|
||||
<div class="team-view cor-container">
|
||||
<div class="organization-header" organization="organization" team-name="teamname">
|
||||
<div ng-show="canEditMembers" class="side-controls">
|
||||
<div class="hidden-xs">
|
||||
<button class="btn btn-success"
|
||||
id="showAddMember"
|
||||
data-title="Add Team Member"
|
||||
data-content-template="/static/directives/team-view-add.html"
|
||||
data-placement="bottom-right"
|
||||
bs-popover="bs-popover">
|
||||
<i class="fa fa-plus"></i>
|
||||
Add Team Member
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="resource-view" resource="membersResource" error-message="'No matching team found'">
|
||||
<div class="description markdown-input" content="team.description" can-write="organization.is_admin"
|
||||
content-changed="updateForDescription" field-title="'team description'"></div>
|
||||
|
||||
<div class="empty-message" ng-if="!members.length">
|
||||
This team has no members
|
||||
</div>
|
||||
|
||||
<div class="empty-message" ng-if="members.length && !(members | filter:search).length">
|
||||
No matching team members found
|
||||
</div>
|
||||
|
||||
<table class="member-listing" style="margin-top: -20px" ng-show="members.length">
|
||||
<!-- Members -->
|
||||
<tr ng-if="(members | filter:search | filter: filterFunction(false, false)).length">
|
||||
<td colspan="2"><div class="section-header">Team Members</div></td>
|
||||
</tr>
|
||||
|
||||
<tr ng-repeat="member in members | filter:search | filter: filterFunction(false, false) | orderBy: 'name'">
|
||||
<td class="user entity">
|
||||
<span class="entity-reference" entity="member" namespace="organization.name" show-avatar="true" avatar-size="32"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="delete-ui" delete-title="'Remove ' + member.name + ' from team'" button-title="'Remove'"
|
||||
perform-delete="removeMember(member.name)" ng-if="canEditMembers"></span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Robots -->
|
||||
<tr ng-if="(members | filter:search | filter: filterFunction(false, true)).length">
|
||||
<td colspan="2"><div class="section-header">Robot Accounts</div></td>
|
||||
</tr>
|
||||
|
||||
<tr ng-repeat="member in members | filter:search | filter: filterFunction(false, true) | orderBy: 'name'">
|
||||
<td class="user entity">
|
||||
<span class="entity-reference" entity="member" namespace="organization.name"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="delete-ui" delete-title="'Remove ' + member.name + ' from team'" button-title="'Remove'"
|
||||
perform-delete="removeMember(member.name)" ng-if="canEditMembers"></span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Invited -->
|
||||
<tr ng-if="(members | filter:search | filter: filterFunction(true, false)).length">
|
||||
<td colspan="2"><div class="section-header">Invited To Join</div></td>
|
||||
</tr>
|
||||
|
||||
<tr ng-repeat="member in members | filter:search | filter: filterFunction(true, false) | orderBy: 'name'">
|
||||
<td class="user entity">
|
||||
<span ng-if="member.kind != 'invite'">
|
||||
<span class="entity-reference" entity="member" namespace="organization.name" show-avatar="true" avatar-size="32"></span>
|
||||
</span>
|
||||
<span class="invite-listing" ng-if="member.kind == 'invite'">
|
||||
<span class="avatar" size="32" data="member.avatar"></span>
|
||||
{{ member.email }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="delete-ui" delete-title="'Revoke invite to join team'" button-title="'Revoke'"
|
||||
perform-delete="revokeInvite(member)" ng-if="canEditMembers"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div ng-show="canEditMembers">
|
||||
<div ng-if-media="'(max-width: 767px)'">
|
||||
<div ng-include="'/static/directives/team-view-add.html'"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="cannotChangeTeamModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Cannot change team</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
You do not have permission to change properties of this team.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="cannotChangeMembersModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Cannot change members</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
You do not have permission to change the members of this team.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
|
@ -1,3 +0,0 @@
|
|||
<div class="cor-container">
|
||||
<div class="angular-tour-ui" tour="tour" inline="true"></div>
|
||||
</div>
|
|
@ -1,127 +0,0 @@
|
|||
<div class="resource-view" resource="orgResource" error-message="'No organization found'"></div>
|
||||
<div class="org-admin cor-container" ng-show="organization">
|
||||
<div class="organization-header" organization="organization" clickable="true"></div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Side tabs -->
|
||||
<div class="col-md-2">
|
||||
<ul class="nav nav-pills nav-stacked cor-tab-shim">
|
||||
<li class="active" quay-require="['BILLING']">
|
||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#plan">Plan and Usage</a>
|
||||
</li>
|
||||
<li quay-classes="{'!Features.BILLING': 'active'}">
|
||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#settings">Organization Settings</a>
|
||||
</li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#logs" ng-click="loadLogs()">Usage Logs</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#members" ng-click="loadMembers()">Members</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#robots">Robot Accounts</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#prototypes">Default Permissions</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#applications" ng-click="loadApplications()">Applications</a></li>
|
||||
<li ng-show="hasPaidPlan" quay-require="['BILLING']">
|
||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#billingoptions">Billing</a>
|
||||
</li>
|
||||
<li ng-show="hasPaidPlan" quay-require="['BILLING']">
|
||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#billing" ng-click="loadInvoices()">Billing History</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="col-md-10">
|
||||
<div class="tab-content">
|
||||
<!-- Plans tab -->
|
||||
<div id="plan" class="tab-pane active" quay-require="['BILLING']">
|
||||
<div class="plan-manager" organization="orgname" plan-changed="planChanged(plan)"></div>
|
||||
</div>
|
||||
|
||||
<!-- Organization settings tab -->
|
||||
<div id="settings" class="tab-pane" quay-classes="{'!Features.BILLING': 'active'}">
|
||||
<div class="quay-spinner" ng-show="changingOrganization"></div>
|
||||
|
||||
<div class="panel" ng-show="!changingOrganization">
|
||||
<div class="panel-title">Organization's e-mail address</div>
|
||||
<div class="panel-content" style="padding-left: 20px; margin-top: 10px;">
|
||||
<form class="form-change" id="changeEmailForm" name="changeEmailForm" ng-submit="changeEmail()" data-trigger="manual"
|
||||
data-content="{{ changeEmailError }}" data-placement="bottom" ng-show="!updatingOrganization">
|
||||
<input type="email" class="form-control" ng-model="organizationEmail"
|
||||
style="margin-left: 10px; margin-right: 10px; width: 400px; display: inline-block;" required>
|
||||
<button class="btn btn-primary" type="submit" ng-disabled="changeEmailForm.$invalid || organizationEmail == organization.email">
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Robot accounts tab -->
|
||||
<div id="robots" class="tab-pane">
|
||||
<div class="robots-manager" organization="organization" is-enabled="true"></div>
|
||||
</div>
|
||||
|
||||
<!-- Prototypes tab -->
|
||||
<div id="prototypes" class="tab-pane">
|
||||
<div class="prototype-manager" organization="organization"></div>
|
||||
</div>
|
||||
|
||||
<!-- Logs tab -->
|
||||
<div id="logs" class="tab-pane">
|
||||
<div class="logs-view" organization="organization" makevisible="logsShown"></div>
|
||||
</div>
|
||||
|
||||
<!-- Applications tab -->
|
||||
<div id="applications" class="tab-pane">
|
||||
<div class="application-manager" organization="organization" makevisible="applicationsShown"></div>
|
||||
</div>
|
||||
|
||||
<!-- Billing Options tab -->
|
||||
<div id="billingoptions" class="tab-pane" quay-require="['BILLING']">
|
||||
<div class="billing-options" organization="organization"></div>
|
||||
</div>
|
||||
|
||||
<!-- Billing History tab -->
|
||||
<div id="billing" class="tab-pane" quay-require="['BILLING']">
|
||||
<div class="billing-invoices" organization="organization" makevisible="invoicesShown"></div>
|
||||
</div>
|
||||
|
||||
<!-- Members tab -->
|
||||
<div id="members" class="tab-pane">
|
||||
<div class="quay-spinner" ng-show="membersLoading"></div>
|
||||
<div ng-show="!membersLoading">
|
||||
<div class="side-controls">
|
||||
<div class="result-count">
|
||||
Showing {{(membersFound | filter:search | limitTo:50).length}} of {{(membersFound | filter:search).length}} matching members
|
||||
</div>
|
||||
<div class="filter-input">
|
||||
<input id="member-filter" class="form-control" placeholder="Filter Members" type="text" ng-model="search.$">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<th>User/Robot Account</th>
|
||||
<th>Teams</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
|
||||
<tr ng-repeat="memberInfo in (membersFound | filter:search | limitTo:50)">
|
||||
<td>
|
||||
<span class="entity-reference" entity="memberInfo" namespace="organization.name"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="team-link" ng-repeat="team in memberInfo.teams">
|
||||
<span class="entity-reference" namespace="organization.name" entity="{'kind': 'team', 'name': team}"></span>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="/organization/{{ organization.name }}/logs/{{ memberInfo.name }}" data-title="Member Usage Logs" bs-tooltip="tooltip.title">
|
||||
<i class="fa fa-book"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,7 +0,0 @@
|
|||
<div class="resource-view" resource="memberResource" error-message="'Member not found'">
|
||||
<div class="org-member-logs cor-container">
|
||||
<div class="organization-header" organization="organization" clickable="true"></div>
|
||||
<div class="logs-view" organization="organization" performer="memberInfo"
|
||||
makevisible="organization && memberInfo && ready"></div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,480 +0,0 @@
|
|||
<div class="cor-container" ng-show="deleting"><div class="quay-spinner"></div></div>
|
||||
<div class="resource-view" resource="repository" error-message="'No repository found'"></div>
|
||||
<div class="cor-container repo repo-admin" ng-show="accessDenied">
|
||||
You do not have permission to view this page
|
||||
</div>
|
||||
<div class="cor-container repo repo-admin" ng-show="repo && !deleting">
|
||||
<div class="header row">
|
||||
<a href="{{ '/repository/' + repo.namespace + '/' + repo.name }}" class="back"><i class="fa fa-chevron-left"></i></a>
|
||||
<h3>
|
||||
<span class="repo-circle no-background" repo="repo"></span>
|
||||
<span class="repo-breadcrumb" repo="repo" subsection-icon="'fa-cog'"
|
||||
subsection="'Admin'"></span>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Side tabs -->
|
||||
<div class="col-md-2">
|
||||
<ul class="nav nav-pills nav-stacked cor-tab-shim">
|
||||
<li class="active"><a href="javascript:void(0)" data-toggle="tab" data-target="#permissions">Permissions</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#trigger" ng-click="loadTriggers()"
|
||||
quay-show="Features.BUILD_SUPPORT">Build Triggers</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#badge">Status Badge</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#notification" ng-click="loadNotifications()">Notifications</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#publicprivate">Public/Private</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#delete">Delete</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#logs" ng-click="loadLogs()">Usage Logs</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="col-md-10">
|
||||
|
||||
<div class="tab-content">
|
||||
<!-- Logs tab -->
|
||||
<div id="logs" class="tab-pane">
|
||||
<div class="logs-view" repository="repo" makevisible="logsShown"></div>
|
||||
</div>
|
||||
|
||||
<!-- Badge tab -->
|
||||
<div id="badge" class="tab-pane">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Status Badge
|
||||
<i class="info-icon fa fa-info-circle" data-placement="left" data-content="Embeddable widget for displaying the status of the repository"></i>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="alert alert-warning" ng-if="!repo.is_public">
|
||||
Note: This repository is currently <b>private</b>. Publishing this badge will reveal the status information of your repository (and links may
|
||||
not work for unregistered users).
|
||||
</div>
|
||||
|
||||
<!-- Status Image -->
|
||||
<a ng-href="/repository/{{ repo.namespace }}/{{ repo.name }}" ng-if="repo && repo.name">
|
||||
<img ng-src="/repository/{{ repo.namespace }}/{{ repo.name }}/status?token={{ repo.status_token }}" data-title="Docker Repository on Quay.io">
|
||||
</a>
|
||||
|
||||
<!-- Embed formats -->
|
||||
<table style="margin-top: 20px; width: 600px;">
|
||||
<thead>
|
||||
<th style="width: 150px"></th>
|
||||
<th></th>
|
||||
</thead>
|
||||
<tr>
|
||||
<td>Image (SVG):</td>
|
||||
<td>
|
||||
<div class="copy-box" hovering-message="true" value="getBadgeFormat('svg', repo)"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Markdown:</td>
|
||||
<td>
|
||||
<div class="copy-box" hovering-message="true" value="getBadgeFormat('md', repo)"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AsciiDoc:</td>
|
||||
<td>
|
||||
<div class="copy-box" hovering-message="true" value="getBadgeFormat('asciidoc', repo)"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Permissions tab -->
|
||||
<div id="permissions" class="tab-pane active">
|
||||
<!-- User Access Permissions -->
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Access Permissions
|
||||
|
||||
<i class="info-icon fa fa-info-circle" data-placement="left" data-content="Allow any number of users, robot accounts or teams to read, write or administer this repository"></i>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<!-- Throbber -->
|
||||
<span class="quay-spinner" ng-show="permissions.loading > 0"></span>
|
||||
<table class="permissions" ng-show="permissions.loading <= 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<td style="min-width: 400px;">User<span ng-show="repo.is_organization">/Team</span>/Robot Account</td>
|
||||
<td>Permissions</td>
|
||||
<td style="width: 95px;"></td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<!-- Team Permissions -->
|
||||
<tr ng-repeat="(name, permission) in permissions['team']">
|
||||
<td class="team entity">
|
||||
<span class="entity-reference" namespace="repo.namespace"
|
||||
entity="buildEntityForPermission(name, permission, 'team')">
|
||||
</span>
|
||||
</td>
|
||||
<td class="user-permissions">
|
||||
<span class="role-group" current-role="permission.role"
|
||||
role-changed="setRole(role, name, 'team')"
|
||||
roles="repoRoles"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="delete-ui" delete-title="'Delete Permission'" perform-delete="deleteRole(name, 'team')"></span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- User Permissions -->
|
||||
<tr ng-repeat="(name, permission) in permissions['user']">
|
||||
<td class="{{ 'user entity ' + (permission.is_org_member ? '' : 'outside') }}">
|
||||
<span class="entity-reference" namespace="repo.namespace"
|
||||
entity="buildEntityForPermission(name, permission, 'user')">
|
||||
</span>
|
||||
</td>
|
||||
<td class="user-permissions">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<span class="role-group" current-role="permission.role"
|
||||
role-changed="setRole(role, name, 'user')" roles="repoRoles"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="delete-ui" delete-title="'Delete Permission'" perform-delete="deleteRole(name, 'user')"></span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td id="add-entity-permission" colspan="2" class="admin-search">
|
||||
<span class="entity-search" namespace="repo.namespace"
|
||||
placeholder="'Add a ' + (repo.is_organization ? 'team or ' : '') + 'user...'"
|
||||
entity-selected="addNewPermission(entity)"
|
||||
current-entity="selectedEntity"
|
||||
auto-clear="true"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Token Permissions -->
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Access Token Permissions
|
||||
|
||||
<i class="info-icon fa fa-info-circle" data-placement="left" data-content="Grant permissions to this repository by creating unique tokens that can be used without entering account passwords<br><br>To use in docker:<br><dl class='dl-horizontal'><dt>Username</dt><dd>$token</dd><dt>Password</dt><dd>(token value)</dd><dt>Email</dt><dd>(any value)</dd></dl>"></i>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form name="createTokenForm" ng-submit="createToken()">
|
||||
<table class="permissions">
|
||||
<thead>
|
||||
<tr>
|
||||
<td style="min-width: 400px;">Token Description</td>
|
||||
<td>Permissions</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr ng-repeat="(code, token) in tokens">
|
||||
<td class="user token">
|
||||
<i class="fa fa-key"></i>
|
||||
<a ng-click="showToken(token.code)">{{ token.friendlyName }}</a>
|
||||
</td>
|
||||
<td class="user-permissions">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" class="btn btn-default" ng-click="changeTokenAccess(token.code, 'read')" ng-class="{read: 'active', write: ''}[token.role]">Read only</button>
|
||||
<button type="button" class="btn btn-default" ng-click="changeTokenAccess(token.code, 'write')" ng-class="{read: '', write: 'active'}[token.role]">Write</button>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="delete-ui" delete-title="'Delete Token'" perform-delete="deleteToken(token.code)"></span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="admin-search">
|
||||
<input type="text" class="form-control" placeholder="New token description" ng-model="newTokenName" required>
|
||||
</td>
|
||||
<td class="admin-search">
|
||||
<button type="submit" ng-disabled="createTokenForm.$invalid" class="btn btn-sm btn-default">Create</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notification tab -->
|
||||
<div id="notification" class="tab-pane">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Repository Notifications
|
||||
<i class="info-icon fa fa-info-circle" data-placement="left" data-content="Notifications to call to external services (web, email, etc) on repository events"></i>
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<!-- Notifications list -->
|
||||
<div class="resource-view" resource="notificationsResource" error-message="'Could not load notifications'">
|
||||
<div class="empty" ng-if="!notifications.length">
|
||||
There are no notifications defined for this repository
|
||||
</div>
|
||||
<div class="nonempty" ng-show="notifications.length">
|
||||
<div class="external-notification-view" notification="notification" repository="repo"
|
||||
notification-deleted="handleNotificationDeleted(notification)"
|
||||
ng-repeat="notification in notifications"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right controls -->
|
||||
<div class="right-controls">
|
||||
<button class="btn btn-success" ng-click="showNewNotificationDialog()">
|
||||
<i class="fa fa-paper-plane"></i>
|
||||
New Notification
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Triggers tab -->
|
||||
<div id="trigger" class="tab-pane" quay-show="['BUILD_SUPPORT']">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">Build Triggers
|
||||
<i class="info-icon fa fa-info-circle" data-placement="left" data-content="Triggers from various services (such as GitHub) which tell the repository to be built and updated."></i>
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<!-- Resource view -->
|
||||
<div class="resource-view" resource="triggersResource" error-message="'Could not load build triggers'">
|
||||
<div ng-show="!triggers.length">No build triggers defined for this repository</div>
|
||||
|
||||
<table class="permissions" ng-show="triggers.length">
|
||||
<thead>
|
||||
<tr>
|
||||
<td style="width: 562px;">Trigger</td>
|
||||
<td style="width: 104px;"></td>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr ng-repeat="trigger in triggers">
|
||||
<td>
|
||||
<div ng-show="!trigger.is_active" style="color: #444;">
|
||||
<span class="quay-spinner" style="vertical-align: middle; margin-right: 6px;"></span>
|
||||
Setting up trigger
|
||||
</div>
|
||||
<div ng-show="trigger.is_active" class="trigger-description" trigger="trigger"></div>
|
||||
<div class="trigger-pull-credentials" ng-if="trigger.is_active && trigger.pull_robot">
|
||||
<span class="context-tooltip" data-title="The credentials used by the builder when pulling images" bs-tooltip>
|
||||
Pull Credentials:
|
||||
</span>
|
||||
<span class="entity-reference" entity="trigger.pull_robot"></span>
|
||||
</div>
|
||||
</td>
|
||||
<td style="white-space: nowrap;" valign="top">
|
||||
<div class="dropdown" style="display: inline-block" ng-visible="trigger.is_active">
|
||||
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" data-title="Build History" bs-tooltip="tooltip.title" data-container="body"
|
||||
ng-click="loadTriggerBuildHistory(trigger)">
|
||||
<i class="fa fa-tasks"></i>
|
||||
<b class="caret"></b>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right pull-right">
|
||||
<li ng-show="trigger.$loadingHistory" style="text-align: center"><span class="quay-spinner" style="padding: 4px;"></span></li>
|
||||
<li role="presentation" class="dropdown-header" ng-show="!trigger.$loadingHistory && !trigger.$builds.length">No builds have been triggered</li>
|
||||
<li role="presentation" class="dropdown-header" ng-show="!trigger.$loadingHistory && trigger.$builds.length">Build History</li>
|
||||
<li ng-repeat="buildInfo in trigger.$builds">
|
||||
<div class="build-info clickable" ng-click="showBuild(buildInfo)">
|
||||
<span class="build-status" build="buildInfo"></span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="dropdown" style="display: inline-block">
|
||||
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown" data-title="Trigger Settings" bs-tooltip="tooltip.title" data-container="body">
|
||||
<i class="fa fa-cog"></i>
|
||||
<b class="caret"></b>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right pull-right">
|
||||
<li><a href="javascript:void(0)" ng-click="setupTrigger(trigger)" ng-show="!trigger.is_active"><i class="fa ci-robot"></i>Resume Setup</a></li>
|
||||
<li><a href="javascript:void(0)" ng-click="startTrigger(trigger)" ng-show="trigger.is_active"><i class="fa fa-tasks"></i>Build Now</a></li>
|
||||
<li><a href="javascript:void(0)" ng-click="deleteTrigger(trigger)"><i class="fa fa-times"></i>Delete Trigger</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- Right controls -->
|
||||
<div class="right-controls">
|
||||
<span ng-show="!Features.GITHUB_BUILD" class="pull-left">No build trigger types enabled.</span>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" data-toggle="dropdown" ng-disabled="!Features.GITHUB_BUILD">
|
||||
New Trigger
|
||||
<b class="caret"></b>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right pull-right">
|
||||
<li>
|
||||
<a href="{{ TriggerService.getRedirectUrl('github', repo.namespace, repo.name) }}">
|
||||
<i class="fa fa-github fa-lg"></i>
|
||||
GitHub <span ng-if="KeyService.isEnterprise('github-trigger')">Enterprise</span> - Repository Push
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Public/private tab -->
|
||||
<div id="publicprivate" class="tab-pane">
|
||||
<!-- Public/Private -->
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div class="repo-access-state" ng-show="!repo.is_public">
|
||||
<div class="state-icon"><i class="fa fa-lock"></i></div>
|
||||
|
||||
This repository is currently <b>private</b>. Only users on the permissions list may view and interact with it.
|
||||
|
||||
<div class="change-access">
|
||||
<button class="btn btn-danger" ng-click="askChangeAccess('public')">Make Public</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="repo-access-state" ng-show="repo.is_public">
|
||||
<div class="state-icon"><i class="fa fa-unlock"></i></div>
|
||||
|
||||
This repository is currently <b>public</b> and is visible to all users, and may be pulled by all users.
|
||||
|
||||
<div class="change-access">
|
||||
<button class="btn btn-danger" ng-click="askChangeAccess('private')">Make Private</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete tab -->
|
||||
<div id="delete" class="tab-pane">
|
||||
<!-- Delete Repo -->
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div class="repo-delete">
|
||||
<div class="alert alert-danger">Deleting a repository <b>cannot be undone</b>. Here be dragons!</div>
|
||||
<button class="btn btn-danger" ng-click="askDelete()">Delete Repository</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Auth dialog -->
|
||||
<div class="docker-auth-dialog" username="'$token'" token="shownToken.code"
|
||||
shown="!!shownToken" counter="shownTokenCounter">
|
||||
<i class="fa fa-key"></i> {{ shownToken.friendlyName }}
|
||||
</div>
|
||||
|
||||
<!-- Setup trigger dialog-->
|
||||
<div class="setup-trigger-dialog" repository="repo"
|
||||
trigger="currentSetupTrigger"
|
||||
canceled="cancelSetupTrigger(trigger)"
|
||||
counter="showTriggerSetupCounter"></div>
|
||||
|
||||
<!-- New notification dialog-->
|
||||
<div class="create-external-notification-dialog" repository="repo"
|
||||
counter="showNewNotificationCounter"
|
||||
notification-created="handleNotificationCreated(notification)"></div>
|
||||
|
||||
<!-- Manual trigger dialog -->
|
||||
<div class="manual-trigger-build-dialog" repository="repo"
|
||||
trigger="currentStartTrigger"
|
||||
counter="showManualBuildDialog"
|
||||
start-build="startTrigger(trigger, parameters)"></div>
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="makepublicModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Make Repository Public</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-warning">
|
||||
Warning: This will allow <b>anyone</b> to pull from this repository
|
||||
</div>
|
||||
Are you sure you want to make this repository public?
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" ng-click="changeAccess('public')">Make Public</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="makeprivateModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Make Repository Private</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-warning">
|
||||
Warning: Only users on the permissions list will be able to access this repository.
|
||||
</div>
|
||||
Are you sure you want to make this repository private?
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-success" ng-click="changeAccess('private')">Make Private</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="confirmdeleteModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Delete Repository?</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Are you <b>absolutely, positively</b> sure you would like to delete this repository? This <b>cannot be undone</b>.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" ng-click="deleteRepo()">Delete Repository</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="confirmaddoutsideModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Add User?</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
The selected user is outside of your organization. Are you sure you want to grant the user access to this repository?
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" ng-click="grantRole()">Yes, I'm sure</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
<div class="resource-view" resource="repository" error-message="'No repository found'"></div>
|
||||
<div class="cor-container repo repo-build" ng-show="accessDenied">
|
||||
You do not have permission to view this page
|
||||
</div>
|
||||
<div class="cor-container repo repo-build" ng-show="repo">
|
||||
<div class="header row">
|
||||
<a href="{{ '/repository/' + repo.namespace + '/' + repo.name }}" class="back"><i class="fa fa-chevron-left"></i></a>
|
||||
<h3>
|
||||
<span class="repo-circle no-background" repo="repo"></span>
|
||||
<span class="repo-breadcrumb" repo="repo" subsection-icon="'fa-tasks'" subsection="'Build History'"></span>
|
||||
</h3>
|
||||
|
||||
<div class="repo-controls">
|
||||
<button class="btn btn-success" ng-click="showNewBuildDialog()">
|
||||
<i class="fa fa-plus"></i>
|
||||
New Dockerfile Build
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="!builds">
|
||||
<div class="quay-spinner"></div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="builds && !builds.length">
|
||||
There are no builds for this repository
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="builds.length">
|
||||
<!-- Side tabs -->
|
||||
<div class="col-sm-2">
|
||||
<ul class="nav nav-pills nav-stacked cor-tab-shim">
|
||||
<li ng-class="currentBuild == build ? 'active' : ''" ng-repeat="build in builds">
|
||||
<a class="build-tab-link" href="javascript:void(0)" ng-click="setCurrentBuild(build.id, true)">
|
||||
<span class="phase-icon" ng-class="build.phase"></span>
|
||||
<span>{{ build.display_name }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="col-sm-10">
|
||||
<div class="tab-content" onresize="adjustLogHeight()">
|
||||
<div ng-repeat="build in builds" class="tab-pane build-pane" ng-class="currentBuild == build ? 'active' : ''">
|
||||
<div class="alert alert-info" ng-show="build.trigger">
|
||||
Triggered by: <span class="trigger-description" trigger="build.trigger" short="true" style="margin-left: 10px"></span>
|
||||
</div>
|
||||
|
||||
<div class="build-header">
|
||||
<div class="timing">
|
||||
<i class="fa fa-clock-o"></i>
|
||||
Started: <span am-time-ago="build.started || 0"></span>
|
||||
<span style="display: inline-block; margin-left: 20px" ng-show="currentBuild.resource_key">
|
||||
<i class="fa fa-archive"></i>
|
||||
<a href="/repository/{{ repo.namespace }}/{{ repo.name }}/build/{{ currentBuild.id }}/buildpack"
|
||||
style="display: inline-block; margin-left: 6px" bs-tooltip="tooltip.title"
|
||||
data-title="View the uploaded build package for this build">Build Package</a>
|
||||
</span>
|
||||
</div>
|
||||
<span class="phase-icon" ng-class="build.phase"></span>
|
||||
<span class="build-message" phase="build.phase"></span>
|
||||
<div class="build-progress" build="build"></div>
|
||||
</div>
|
||||
|
||||
<div class="build-logs">
|
||||
<div ng-show="!logEntries">
|
||||
<span class="quay-spinner"></span>
|
||||
</div>
|
||||
|
||||
<div class="log-container" ng-class="container.type" ng-repeat="container in logEntries">
|
||||
<div class="container-header" ng-class="container.type == 'phase' ? container.message : ''"
|
||||
ng-switch on="container.type" ng-click="container.logs.toggle()">
|
||||
<i class="fa chevron"
|
||||
ng-class="container.logs.isVisible ? 'fa-chevron-down' : 'fa-chevron-right'" ng-show="hasLogs(container)"></i>
|
||||
<div ng-switch-when="phase">
|
||||
<span class="container-content build-log-phase" phase="container"></span>
|
||||
</div>
|
||||
<div ng-switch-when="error">
|
||||
<span class="container-content build-log-error" error="container" entries="logEntries"></span>
|
||||
</div>
|
||||
<div ng-switch-when="command">
|
||||
<span class="container-content build-log-command" command="container"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Display the entries for the container -->
|
||||
<div class="container-logs" ng-show="container.logs.isVisible">
|
||||
<div class="log-entry" bindonce ng-repeat="entry in container.logs.visibleEntries">
|
||||
<span class="id" bo-text="$index + container.index + 1"></span>
|
||||
<span class="message" bo-html="processANSI(entry.message, container)"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 10px">
|
||||
<button class="btn btn-default"
|
||||
ng-show="build.phase == 'waiting' && build.resource_key"
|
||||
ng-click="askCancelBuild(build)">
|
||||
<i class="fa fa-times-circle" style="margin-right: 6px; display: inline-block;"></i>
|
||||
Cancel Build
|
||||
</button>
|
||||
|
||||
<button class="btn" ng-show="(build.phase == 'error' || build.phase == 'complete') && build.resource_key"
|
||||
ng-class="build.phase == 'error' ? 'btn-success' : 'btn-default'"
|
||||
ng-click="askRestartBuild(build)">
|
||||
<i class="fa fa-refresh"></i>
|
||||
Run Build Again
|
||||
</button>
|
||||
<span class="quay-spinner" ng-show="pollChannel.working"></span>
|
||||
<span class="build-id">{{ build.id }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dockerfile-build-dialog" show-now="buildDialogShowCounter" repository="repo"
|
||||
build-started="handleBuildStarted(build)">
|
||||
</div>
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="confirmRestartBuildModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Run Dockerfile Build?</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Are you sure you want to run this Dockerfile build again? The results will be immediately pushed to the repository.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" ng-click="restartBuild(currentBuild)">Run Build</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
</div>
|
|
@ -1,436 +0,0 @@
|
|||
<div class="loading" ng-show="!user">
|
||||
<div class="quay-spinner"></div>
|
||||
</div>
|
||||
|
||||
<div class="loading" ng-show="user.anonymous">
|
||||
No matching user found
|
||||
</div>
|
||||
|
||||
<div class="user-admin cor-container" ng-show="!user.anonymous">
|
||||
<div class="row">
|
||||
<div class="organization-header-element">
|
||||
<span class="avatar" size="24" data="user.avatar"></span>
|
||||
<span class="organization-name">
|
||||
{{ user.username }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Side tabs -->
|
||||
<div class="col-md-2">
|
||||
<ul class="nav nav-pills nav-stacked cor-tab-shim">
|
||||
<!-- Billing Related -->
|
||||
<li class="active" quay-require="['BILLING']"><a href="javascript:void(0)" data-toggle="tab" data-target="#plan">Plan and Usage</a></li>
|
||||
<li ng-show="hasPaidPlan" quay-require="['BILLING']">
|
||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#billingoptions">Billing Options</a>
|
||||
</li>
|
||||
<li ng-show="hasPaidPlan" quay-require="['BILLING']">
|
||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#billing" ng-click="loadInvoices()">Billing History</a>
|
||||
</li>
|
||||
|
||||
<!-- Non-billing -->
|
||||
<li quay-classes="{'!Features.BILLING': 'active'}"><a href="javascript:void(0)" data-toggle="tab" data-target="#email">Account E-mail</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#robots">Robot Accounts</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#password">Password</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#external" quay-show="Features.GITHUB_LOGIN || Features.GOOGLE_LOGIN">External Logins</a></li>
|
||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#authorized" ng-click="loadAuthedApps()">Authorized Applications</a></li>
|
||||
<li quay-show="Features.USER_LOG_ACCESS || hasPaidBusinessPlan">
|
||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#logs" ng-click="loadLogs()">Usage Logs</a>
|
||||
</li>
|
||||
<li quay-require="['USER_RENAME']">
|
||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#username">Change Username</a>
|
||||
</li>
|
||||
<li quay-show="Config.AUTHENTICATION_TYPE == 'Database'">
|
||||
<a href="javascript:void(0)" data-toggle="tab" data-target="#migrate" id="migrateTab">Convert to Organization</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="col-md-10">
|
||||
<div class="tab-content">
|
||||
<!-- Authorized applications tab -->
|
||||
<div id="authorized" class="tab-pane">
|
||||
<div class="quay-spinner" ng-show="!authorizedApps"></div>
|
||||
|
||||
<div class="panel" ng-show="authorizedApps != null">
|
||||
<div class="panel-body" ng-show="!authorizedApps.length">
|
||||
You have not authorized any external applications
|
||||
</div>
|
||||
<div class="panel-body" ng-show="authorizedApps.length">
|
||||
<div class="alert alert-info">
|
||||
These are the applications you have authorized to view information and perform actions on your behalf.
|
||||
</div>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th>Application Name</th>
|
||||
<th>Authorized Permissions</th>
|
||||
<th style="width: 150px">Revoke</th>
|
||||
</thead>
|
||||
|
||||
<tr class="auth-info" ng-repeat="authInfo in authorizedApps">
|
||||
<td>
|
||||
<span class="avatar" size="16" data="authInfo.application.avatar"></span>
|
||||
<a href="{{ authInfo.application.url }}" ng-if="authInfo.application.url" target="_blank"
|
||||
data-title="{{ authInfo.application.description || authInfo.application.name }}" bs-tooltip>
|
||||
{{ authInfo.application.name }}
|
||||
</a>
|
||||
<span ng-if="!authInfo.application.url" data-title="{{ authInfo.application.description || authInfo.application.name }}" bs-tooltip>
|
||||
{{ authInfo.application.name }}
|
||||
</span>
|
||||
<span class="by">{{ authInfo.application.organization.name }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-default scope"
|
||||
ng-class="{'repo:admin': 'label-primary', 'repo:write': 'label-success', 'repo:create': 'label-success'}[scopeInfo.scope]"
|
||||
ng-repeat="scopeInfo in authInfo.scopes" data-title="{{ scopeInfo.description }}" bs-tooltip>
|
||||
{{ scopeInfo.scope }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="delete-ui" delete-title="'Revoke Authorization'" button-title="'Revoke'" perform-delete="deleteAccess(authInfo)"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Logs tab -->
|
||||
<div id="logs" class="tab-pane">
|
||||
<div class="logs-view" user="user" makevisible="logsShown"></div>
|
||||
</div>
|
||||
|
||||
<!-- Plans tab -->
|
||||
<div id="plan" class="tab-pane active" quay-require="['BILLING']">
|
||||
<div class="plan-manager" user="user.username" ready-for-plan="readyForPlan()" plan-changed="planChanged(plan)"></div>
|
||||
</div>
|
||||
|
||||
<!-- E-mail address tab -->
|
||||
<div id="email" class="tab-pane" quay-classes="{'!Features.BILLING': 'active'}">
|
||||
<div class="row">
|
||||
<div class="alert alert-success" ng-show="changeEmailSent">An e-mail has been sent to {{ sentEmail }} to verify the change.</div>
|
||||
|
||||
<div class="loading" ng-show="updatingUser">
|
||||
<div class="quay-spinner 3x"></div>
|
||||
</div>
|
||||
|
||||
<div class="panel" ng-show="!updatingUser">
|
||||
<div class="panel-title">Account e-mail address</div>
|
||||
<div class="panel-setting-content panel-body">
|
||||
{{ user.email }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel" ng-show="!updatingUser" quay-show="Features.MAILING">
|
||||
<div class="panel-title">Change e-mail address</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<form class="form-change col-md-6" id="changeEmailForm" name="changeEmailForm" ng-submit="changeEmail()"
|
||||
ng-show="!awaitingConfirmation && !registering">
|
||||
<input type="email" class="form-control" placeholder="Your new e-mail address" ng-model="cuser.email" required>
|
||||
<button class="btn btn-primary" ng-disabled="changeEmailForm.$invalid || cuser.email == user.email" type="submit">Change E-mail Address</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Password tab -->
|
||||
<div id="password" class="tab-pane">
|
||||
<!-- Encrypted Password -->
|
||||
<div class="row">
|
||||
<div class="panel">
|
||||
<div class="panel-title">Generate Encrypted Password</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<div class="alert alert-info" ng-if="!Features.REQUIRE_ENCRYPTED_BASIC_AUTH">
|
||||
Due to Docker storing passwords entered on the command line in <strong>plaintext</strong>, it is highly recommended to use the button below to generate an an encrypted version of your password.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning" ng-if="Features.REQUIRE_ENCRYPTED_BASIC_AUTH">
|
||||
This installation is set to <strong>require</strong> encrypted passwords when
|
||||
using the Docker command line interface. To generate an encrypted password, click the button below.
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" ng-click="generateClientToken()">
|
||||
<i class="fa fa-key" style="margin-right: 6px;"></i>Generate Encrypted Password
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Change Password -->
|
||||
<div class="row">
|
||||
<div class="panel">
|
||||
<div class="panel-title">Change Password</div>
|
||||
|
||||
<div class="loading" ng-show="updatingUser">
|
||||
<div class="quay-spinner 3x"></div>
|
||||
</div>
|
||||
|
||||
<span class="help-block" ng-show="changePasswordSuccess">Password changed successfully</span>
|
||||
|
||||
<div ng-show="!updatingUser" class="panel-body">
|
||||
<div class="alert alert-warning">Note: Changing your password will also invalidate any generated encrypted passwords.</div>
|
||||
|
||||
|
||||
<form class="form-change col-md-6" id="changePasswordForm" name="changePasswordForm" ng-submit="changePassword()"
|
||||
ng-show="!awaitingConfirmation && !registering">
|
||||
<input type="password" class="form-control" placeholder="Your new password" ng-model="cuser.password" required
|
||||
ng-pattern="/^.{8,}$/">
|
||||
<input type="password" class="form-control" placeholder="Verify your new password" ng-model="cuser.repeatPassword"
|
||||
match="cuser.password" required ng-pattern="/^.{8,}$/">
|
||||
<button class="btn btn-danger" ng-disabled="changePasswordForm.$invalid" type="submit"
|
||||
analytics-on analytics-event="change_pass">Change Password</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- External Login tab -->
|
||||
<div id="external" class="tab-pane" quay-show="Features.GITHUB_LOGIN || Features.GOOGLE_LOGIN">
|
||||
<div class="loading" ng-show="!cuser">
|
||||
<div class="quay-spinner 3x"></div>
|
||||
</div>
|
||||
|
||||
<!-- Github -->
|
||||
<div class="row" quay-show="cuser && Features.GITHUB_LOGIN">
|
||||
<div class="panel">
|
||||
<div class="panel-title">GitHub Login:</div>
|
||||
<div class="panel-body">
|
||||
<div ng-show="hasGithubLogin && githubLogin" class="lead col-md-8">
|
||||
<i class="fa fa-github fa-lg" style="margin-right: 6px;" data-title="GitHub" bs-tooltip="tooltip.title"></i>
|
||||
<b><a href="{{githubEndpoint}}{{githubLogin}}" target="_blank">{{githubLogin}}</a></b>
|
||||
<span class="delete-ui" button-title="'Detach'" delete-title="'Detach Account'" style="margin-left: 10px"
|
||||
perform-delete="detachExternalLogin('github')"></span>
|
||||
</div>
|
||||
<div ng-show="hasGithubLogin && !githubLogin" class="lead col-md-8">
|
||||
<i class="fa fa-github fa-lg" style="margin-right: 6px;" data-title="GitHub" bs-tooltip="tooltip.title"></i>
|
||||
Account attached to Github Account
|
||||
<span class="delete-ui" button-title="'Detach'" delete-title="'Detach Account'" style="margin-left: 10px"
|
||||
perform-delete="detachExternalLogin('github')"></span>
|
||||
</div>
|
||||
<div ng-show="!hasGithubLogin" class="col-md-4">
|
||||
<span class="external-login-button" provider="github" action="attach"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Google -->
|
||||
<div class="row" quay-show="cuser && Features.GOOGLE_LOGIN">
|
||||
<div class="panel">
|
||||
<div class="panel-title">Google Login:</div>
|
||||
<div class="panel-body">
|
||||
<div ng-show="hasGoogleLogin && googleLogin" class="lead col-md-8">
|
||||
<i class="fa fa-google fa-lg" style="margin-right: 6px;" data-title="Google" bs-tooltip="tooltip.title"></i>
|
||||
<b>{{ googleLogin }}</b>
|
||||
<span class="delete-ui" button-title="'Detach'" delete-title="'Detach Account'" style="margin-left: 10px"
|
||||
perform-delete="detachExternalLogin('google')"></span>
|
||||
</div>
|
||||
<div ng-show="hasGoogleLogin && !googleLogin" class="lead col-md-8">
|
||||
<i class="fa fa-google fa-lg" style="margin-right: 6px;" data-title="Google" bs-tooltip="tooltip.title"></i>
|
||||
Account attached to Google Account
|
||||
<span class="delete-ui" button-title="'Detach'" delete-title="'Detach Account'" style="margin-left: 10px"
|
||||
perform-delete="detachExternalLogin('google')"></span>
|
||||
</div>
|
||||
<div ng-show="!hasGoogleLogin" class="col-md-4">
|
||||
<span class="external-login-button" provider="google" action="attach"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Robot accounts tab -->
|
||||
<div id="robots" class="tab-pane">
|
||||
<div class="robots-manager" user="user" is-enabled="true"></div>
|
||||
</div>
|
||||
|
||||
<!-- Billing options tab -->
|
||||
<div id="billingoptions" class="tab-pane" quay-require="['BILLING']">
|
||||
<div class="billing-options" user="user"></div>
|
||||
</div>
|
||||
|
||||
<!-- Billing History tab -->
|
||||
<div id="billing" class="tab-pane" quay-require="['BILLING']">
|
||||
<div class="billing-invoices" user="user" makevisible="invoicesShown"></div>
|
||||
</div>
|
||||
|
||||
<!-- Change username tab -->
|
||||
<div id="username" class="tab-pane" quay-show="Features.USER_RENAME">
|
||||
<div class="row">
|
||||
<div class="panel">
|
||||
<div class="panel-title">Change Username</div>
|
||||
|
||||
<div class="loading" ng-show="updatingUser">
|
||||
<div class="quay-spinner 3x"></div>
|
||||
</div>
|
||||
|
||||
<span class="help-block" ng-show="changeUsernameSuccess">Username changed successfully</span>
|
||||
|
||||
<div ng-show="!updatingUser" class="panel-body">
|
||||
<form class="form-change col-md-6" id="changeUsernameForm" name="changeUsernameForm" ng-submit="changePassword()"
|
||||
ng-show="!awaitingConfirmation && !registering">
|
||||
<input type="text" class="form-control" placeholder="Your new username" ng-model="cuser.username" required
|
||||
ng-pattern="/{{ USER_PATTERN }}/">
|
||||
<button class="btn btn-danger" ng-disabled="changeUsernameForm.$invalid" type="submit"
|
||||
analytics-on analytics-event="change_username">Change Username</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Convert to organization tab -->
|
||||
<div id="migrate" class="tab-pane" quay-show="Config.AUTHENTICATION_TYPE == 'Database'">
|
||||
<!-- Step 0 -->
|
||||
<div class="panel" ng-show="convertStep == 0">
|
||||
<div class="panel-body" ng-show="user.organizations.length > 0">
|
||||
<div class="alert alert-info">
|
||||
Cannot convert this account into an organization, as it is a member of {{user.organizations.length}} other
|
||||
organization{{user.organizations.length > 1 ? 's' : ''}}. Please leave
|
||||
{{user.organizations.length > 1 ? 'those organizations' : 'that organization'}} first.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-body" ng-show="user.organizations.length == 0">
|
||||
<div class="alert alert-warning">
|
||||
Note: Converting a user account into an organization <b>cannot be undone</b>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" ng-click="showConvertForm()">Start conversion process</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 1 -->
|
||||
<div class="convert-form" ng-show="convertStep == 1">
|
||||
<h3>Convert to organization</h3>
|
||||
|
||||
<form method="post" name="convertForm" id="convertForm" ng-submit="convertToOrg()">
|
||||
<div class="form-group">
|
||||
<label for="orgName">Organization Name</label>
|
||||
<div class="existing-data">
|
||||
<span class="avatar" size="24" data="user.avatar"></span>
|
||||
{{ user.username }}</div>
|
||||
<span class="description">This will continue to be the namespace for your repositories</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="orgName">Admin User</label>
|
||||
<input id="adminUsername" name="adminUsername" type="text" class="form-control" placeholder="Admin Username"
|
||||
ng-model="org.adminUser" required autofocus>
|
||||
<input id="adminPassword" name="adminPassword" type="password" class="form-control" placeholder="Admin Password"
|
||||
ng-model="org.adminPassword" required>
|
||||
<span class="description">
|
||||
The username and password for the account that will become an administrator of the organization.
|
||||
Note that this account <b>must be a separate registered account</b> from the account that you are
|
||||
trying to convert, and <b>must already exist</b>.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Plans Table -->
|
||||
<div class="form-group plan-group" quay-require="['BILLING']">
|
||||
<label>Organization Plan</label>
|
||||
<div class="plans-table" plans="orgPlans" current-plan="org.plan"></div>
|
||||
</div>
|
||||
|
||||
<div class="button-bar">
|
||||
<button class="btn btn-large btn-danger" type="submit"
|
||||
ng-disabled="convertForm.$invalid || (Features.BILLING && !org.plan)"
|
||||
analytics-on analytics-event="convert_to_organization">
|
||||
Convert To Organization
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="cannotconvertModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Cannot convert account</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Your account could not be converted. Please try again in a moment.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="invalidadminModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Username or password invalid</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
The username or password specified for the admin account is not valid.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="clientTokenModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Encrypted Password</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div style="margin-bottom: 10px;">Your generated encrypted password:</div>
|
||||
<div class="copy-box" value="generatedClientToken"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Dismiss</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
|
||||
<!-- Modal message dialog -->
|
||||
<div class="modal fade" id="reallyconvertModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Convert to organization?</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-danger">You will not be able to login to this account once converted</div>
|
||||
<div>Are you <b>absolutely sure</b> you would like to convert this account to an organization? Once done, there is no going back.</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" data-dismiss="modal" ng-click="reallyConvert()">Absolutely: Convert Now</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
Reference in a new issue