+
+ {% if session['userrole'] == 'admin' and 'admin' in request.path %}
+ Add service
+ {% endif %}
+ Back
+
+ {% if services|length > 0 %}
+
+
+ {% include 'managed_services.html' %}
+
+
+ {% else %}
+
+
+
Empty list
+
No service has been found
+
+
+ {% endif %}
+
+
+{% endblock %}
diff --git a/app/static/css/catalogue.css b/app/static/css/catalogue.css
new file mode 100644
index 000000000..1aa292c92
--- /dev/null
+++ b/app/static/css/catalogue.css
@@ -0,0 +1,148 @@
+ .wrapper {
+ position: relative;
+ overflow: visible;
+ /*
+ top: 5px;
+ right: -5px */
+}
+ .wrapper:after {
+ content: '';
+ display: block;
+ padding-top: 30%;
+}
+ .wrapper img {
+ object-fit: contain;
+ height: 85%;
+ position: absolute;
+ top: 0;
+}
+ .card-img-overlay {
+ /* background-color: #3374FF; */
+ background-color: #213752;
+ color: white;
+ /* font-family: 'Abel', sans-serif;
+ font-family: 'Roboto Condensed', sans-serif; */
+ opacity: 0.90;
+}
+ .popover {
+ border: 2px solid #5BC0DE;
+ max-width: 50%;
+}
+ .popover-header {
+ background-color: #5BC0DE;
+ color: #FFFFFF;
+ margin: 2;
+ /* font-size: 22px; */
+ text-align: center;
+ /* font-family: 'Abel', sans-serif;
+ font-family: 'Roboto Condensed', sans-serif; */
+}
+ .popover-body {
+ /* background-color: white;
+ color: #FFFFFF; */
+ padding: 25px;
+ /* overflow-y: auto;
+ max-height: 150px; */
+}
+/* The ribbons */
+/* Default ribbon is green. */
+ .ribbon {
+ position: absolute;
+ right: 0px;
+ bottom: 0px;
+ z-index: 1;
+ overflow: visible;
+ width: 75px;
+ height: 75px;
+ text-align: right;
+ transform: rotate(-270deg);
+ -webkit-transform: rotate(-270deg);
+}
+ .ribbon span {
+ border-radius: 0px 0px 16px 16px;
+ font-size: 10px;
+ color: #fff;
+ text-transform: uppercase;
+ text-align: center;
+ font-weight: bold;
+ line-height: 20px;
+ transform: rotate(225deg);
+ width: 100px;
+ display: block;
+ background: #79A70A;
+ background: linear-gradient(#79A70A 0%, #9BC90D 100%);
+ box-shadow: 0 -3px 10px -5px rgba(0, 0, 0, 1);
+ position: absolute;
+ top: 19px;
+ right: -21px;
+}
+ .ribbon span::before {
+ content: '';
+ position: absolute;
+ right: 0px;
+ bottom: 100%;
+ z-index: -1;
+ border-left: 3px solid #79A70A;
+ border-right: 3px solid transparent;
+ border-bottom: 3px solid transparent;
+ border-top: 3px solid #79A70A;
+ transform: rotate (180deg);
+ -webkit-transform: rotate(180deg);
+}
+ .ribbon span::after {
+ content: '';
+ position: absolute;
+ left: 0px;
+ bottom: 100%;
+ z-index: -1;
+ border-right: 3px solid #79A70A;
+ border-left: 3px solid transparent;
+ border-bottom: 3px solid transparent;
+ border-top: 3px solid #79A70A;
+ transform: rotate (180deg);
+ -webkit-transform: rotate(180deg);
+}
+ .green span {
+ background: linear-gradient(#79A70A 0%, #9BC90D 100%);
+}
+ .green span::before {
+ border-left-color: #79A70A;
+ border-top-color: #79A70A;
+}
+ .green span::after {
+ border-right-color: #79A70A;
+ border-top-color: #79A70A;
+}
+ .red span {
+ background: linear-gradient(#8F0808 0%, #F70505 100%);
+}
+ .red span::before {
+ border-left-color: #8F0808;
+ border-top-color: #8F0808;
+}
+ .red span::after {
+ border-right-color: #8F0808;
+ border-top-color: #8F0808;
+}
+ .blue span {
+ background: linear-gradient(#1e5799 0%, #2989d8 100%);
+}
+ .blue span::before {
+ border-left-color: #1e5799;
+ border-top-color: #1e5799;
+}
+ .blue span::after {
+ border-right-color: #1e5799;
+ border-top-color: #1e5799;
+}
+ .yellow span {
+ background: linear-gradient(#b07620 0%, #ffbf00 100%);
+}
+ .yellow span::before {
+ border-left-color: #b07620;
+ border-top-color: #b07620;
+}
+ .yellow span::after {
+ border-right-color: #b07620;
+ border-top-color: #b07620;
+}
diff --git a/app/static/js/catalogue.js b/app/static/js/catalogue.js
new file mode 100644
index 000000000..0003648ca
--- /dev/null
+++ b/app/static/js/catalogue.js
@@ -0,0 +1,47 @@
+
+$(".service-descr").dotdotdot({
+ ellipsis: ' [...] ',
+ wrap: 'word',
+ after: "a.read_more",
+ watch: true,
+ height: 70,
+ callback: function(isTruncated, orgContent) {
+ if (isTruncated == true) {
+ //$(".read_more").css("visibility","visible");
+ } else {
+ $(this).parent().find('.read_more').css("display", "none");
+ //$(".read_more").css("display","none");
+ }
+ }
+ });
+
+function cardFilter() {
+ var input, filter, cards, cardContainer, h5, title, i;
+ input = document.getElementById("cardFilter");
+ filter = input.value.toUpperCase();
+ cardContainer = document.getElementById("cardsContainer");
+ cards = cardContainer.getElementsByClassName("card");
+ for (i = 0; i < cards.length; i++) {
+ title = cards[i].querySelector(".card-body .card-title");
+ if (title.innerText.toUpperCase().indexOf(filter) > -1) {
+ cards[i].style.display = "";
+ } else {
+ cards[i].style.display = "none";
+ }
+ }
+ };
+
+$(document).ready(function() {
+ $(".card-img-overlay").hide();
+ $(".card").hover(function() {
+ $(this).addClass('shadow-lg').css('cursor', 'pointer');
+ $(this).find('.card-img-overlay').show();
+ }, function() {
+ $(this).removeClass('shadow-lg');
+ $(this).find('.card-img-overlay').hide();
+ });
+ });
+
+$(document).ready(function() {
+ $('[data-toggle="popover"]').popover();
+});
\ No newline at end of file
diff --git a/config/default.py b/config/default.py
index 928dcec7c..255e3eb71 100644
--- a/config/default.py
+++ b/config/default.py
@@ -45,6 +45,9 @@
FEATURE_REQUIRE_USER_SSH_PUBKEY = "no"
FEATURE_PORTS_REQUEST = "no"
FEATURE_S3CREDS_MENU = "no"
+FEATURE_ACCESS_REQUEST = "yes"
+
+ACCESS_REQUEST_TAG = "LOCKED"
### VAULT INTEGRATION SETTINGS
VAULT_ROLE = "orchestrator"
@@ -63,3 +66,5 @@
PORTFOLIO_TEMPLATE = 'portfolio.html'
MAIL_TEMPLATE = 'email.html'
FOOTER_TEMPLATE = 'footer.html'
+
+UPLOAD_FOLDER = '/opt/uploads'
diff --git a/config/infn-cloud.py b/config/infn-cloud.py
index c632d1782..09b3c7972 100644
--- a/config/infn-cloud.py
+++ b/config/infn-cloud.py
@@ -8,6 +8,9 @@
FEATURE_REQUIRE_USER_SSH_PUBKEY = "yes"
FEATURE_PORTS_REQUEST = "yes"
+FEATURE_ACCESS_REQUEST = "no"
+
+ACCESS_REQUEST_TAG = "SYS-ADMIN ONLY"
### Template Paths
HOME_TEMPLATE = 'infn-cloud/home.html'
diff --git a/migrations/versions/514c0e87e61f_ninth_update.py b/migrations/versions/514c0e87e61f_ninth_update.py
new file mode 100644
index 000000000..d09cebe77
--- /dev/null
+++ b/migrations/versions/514c0e87e61f_ninth_update.py
@@ -0,0 +1,53 @@
+"""Ninth update
+
+Revision ID: 514c0e87e61f
+Revises: 7e9fa167c199
+Create Date: 2022-06-09 21:43:03.802422
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '514c0e87e61f'
+down_revision = '7e9fa167c199'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('service',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('url', sa.String(length=128), nullable=False),
+ sa.Column('name', sa.String(length=128), nullable=False),
+ sa.Column('icon', sa.String(length=128), nullable=False, server_default=""),
+ sa.Column('description', sa.String(length=2048), nullable=True),
+ sa.Column('visibility', sa.Enum('private', 'public', name='visibility'), nullable=False, server_default='private'),
+ sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.func.now()),
+ sa.Column('updated_at', sa.DateTime(), nullable=False, server_default=sa.func.now(), onupdate=sa.func.now()),
+ sa.PrimaryKeyConstraint('id'),
+ sa.UniqueConstraint('url')
+ )
+ op.create_table('users_group',
+ sa.Column('name', sa.String(length=32), nullable=False),
+ sa.PrimaryKeyConstraint('name')
+ )
+ op.create_table('service_access',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('service_id', sa.Integer(), nullable=True),
+ sa.Column('group_id', sa.String(length=32), nullable=True),
+ sa.ForeignKeyConstraint(['group_id'], ['users_group.name'], ondelete='cascade'),
+ sa.ForeignKeyConstraint(['service_id'], ['service.id'], ondelete='cascade'),
+ sa.PrimaryKeyConstraint('id')
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('service_access')
+ op.drop_table('users_group')
+ op.drop_table('service')
+ # ### end Alembic commands ###