[Monitor Dashboard|Trunk]
Description: add additional description field add tooltip to show description Manage stakeholders: add checks for roles git-svn-id: https://svn.driver.research-infrastructures.eu/driver/dnet40/modules/uoa-monitor-portal/trunk/monitor_dashboard@59713 d315682c-612b-4755-9ff5-7f18f6832af3
This commit is contained in:
parent
f68b7187e9
commit
df36b36b5d
|
@ -100,8 +100,8 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||
this.stakeholderService.getStakeholder(this.properties.monitorServiceAPIURL, params['stakeholder']).subscribe(stakeholder => {
|
||||
if(stakeholder) {
|
||||
this.stakeholder = stakeholder;
|
||||
this.buildMenu();
|
||||
LinksResolver.setProperties(this.stakeholder.alias);
|
||||
this.buildMenu();
|
||||
this.stakeholderService.setStakeholder(stakeholder);
|
||||
this.layoutService.setSmallScreen((this.innerWidth && this.innerWidth < 1219));
|
||||
this.layoutService.setOpen(!(this.innerWidth && this.innerWidth < 1219));
|
||||
|
@ -209,13 +209,13 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||
position: 'center',
|
||||
badge: false
|
||||
};
|
||||
// if(this.isAdmin()) {
|
||||
if(this.isAdmin()) {
|
||||
this.menuItems.push({
|
||||
rootItem: new MenuItem("manage", "Manage",
|
||||
"", "/admin", false, [], null, {}), items: []
|
||||
});
|
||||
|
||||
// }
|
||||
}
|
||||
this.specialSideBarMenuItem = new MenuItem("search", "Search research outcomes", "", this.properties.searchLinkToResults, false, [], null, {})
|
||||
this.specialSideBarMenuItem.icon = '<span uk-icon="search"></span>';
|
||||
|
||||
|
@ -282,7 +282,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
public isAdmin() {
|
||||
return this.user && (Session.isPortalAdministrator(this.user) || Session.isCommunityCurator(this.user) || Session.isMonitorCurator(this.user));
|
||||
return this.user && (Session.isPortalAdministrator(this.user) || Session.isCommunityCurator(this.user) || Session.isMonitorCurator(this.user) || (this.stakeholder && Session.isManager(this.stakeholder.type,this.stakeholder.alias,this.user)));
|
||||
}
|
||||
|
||||
public isPublicOrIsMember(visibility: Visibility): boolean {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<div id="page_content">
|
||||
<div id="page_content_inner">
|
||||
<div>
|
||||
<div *ngIf="isCurator()">
|
||||
<ul class="uk-tab customTabs admin" uk-tab>
|
||||
<li [class.uk-active]="tab === 'managers'"><a (click)="changeTab('all')"><span class="title">All</span></a></li>
|
||||
<li *ngIf="isAdministrator()" [class.uk-active]="tab === 'members'"><a (click)="changeTab('templates')"><span class="title">Profile templates</span></a></li>
|
||||
<li [class.uk-active]="tab === 'members'"><a (click)="changeTab('templates')"><span class="title">Profile templates</span></a></li>
|
||||
<li [class.uk-active]="tab === 'members'"><a (click)="changeTab('profiles')"><span class="title">Profiles</span></a></li>
|
||||
</ul>
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="tab != 'profiles' && isAdministrator()" class="uk-margin-top">
|
||||
<div *ngIf="tab != 'profiles' && isCurator()" class="uk-margin-top">
|
||||
<h6 class="main">Profile Templates</h6>
|
||||
<div class = "uk-child-width-1-4@m uk-child-width-1-3@s uk-grid-match uk-grid-small"
|
||||
uk-grid>
|
||||
|
@ -31,13 +31,13 @@
|
|||
<ng-template ngFor [ngForOf]="displayDefaultStakeholders" let-stakeholder let-i="index">
|
||||
<ng-container *ngTemplateOutlet="stakeholderBox; context: {stakeholder:stakeholder}"></ng-container>
|
||||
</ng-template>
|
||||
<div *ngIf="!loading">
|
||||
<div *ngIf="!loading && isCurator()">
|
||||
<ng-container *ngTemplateOutlet="newBox; context:
|
||||
{text:'Create a new default profile.', isDefault:true}"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="tab != 'templates'" class="uk-margin-top">
|
||||
<div *ngIf="tab != 'templates' && isManager()" class="uk-margin-top">
|
||||
<h6 class="">Profiles</h6>
|
||||
<div class="uk-grid-match uk-grid-small uk-child-width-1-4@m uk-child-width-1-3@s"
|
||||
uk-grid>
|
||||
|
@ -45,7 +45,7 @@
|
|||
<ng-template ngFor [ngForOf]="displayStakeholders" let-stakeholder let-i="index">
|
||||
<ng-container *ngTemplateOutlet="stakeholderBox; context: {stakeholder:stakeholder}"></ng-container>
|
||||
</ng-template>
|
||||
<div *ngIf="!loading">
|
||||
<div *ngIf="!loading && isCurator()">
|
||||
<ng-container *ngTemplateOutlet="newBox; context:
|
||||
{text:'Create a new profile by selecting the type (Funder, Organization, Research Initiative or Project) and '+
|
||||
' select indicators based on a default or a blank profile.', isDefault:false}"></ng-container>
|
||||
|
@ -73,8 +73,8 @@
|
|||
{{'Make ' + v.label.toLowerCase()}}</a>
|
||||
</li>
|
||||
</ng-template>
|
||||
<hr *ngIf="isAdministrator()" class="uk-nav-divider">
|
||||
<li *ngIf="isAdministrator()"><a
|
||||
<hr *ngIf="isProfileManager(stakeholder)" class="uk-nav-divider">
|
||||
<li *ngIf="isProfileManager(stakeholder)"><a
|
||||
(click)="$event.stopPropagation();deleteStakeholderOpen(stakeholder);hide(element);$event.preventDefault()">Delete</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -232,8 +232,16 @@ export class ManageStakeholdersComponent implements OnInit, OnDestroy {
|
|||
stakeholder.isPublic = isPublic;
|
||||
});
|
||||
}
|
||||
|
||||
public isAdministrator(): boolean {
|
||||
public isManager(): boolean {
|
||||
return this.isCurator() || (Session.isKindOfMonitorManager(this.user));
|
||||
}
|
||||
public isProfileManager(stakeholder:Stakeholder): boolean {
|
||||
return this.isCurator() || (Session.isManager(stakeholder.type, stakeholder.alias, this.user));
|
||||
}
|
||||
public isCurator(): boolean {
|
||||
return this.isAdmin() || Session.isCommunityCurator(this.user) || Session.isMonitorCurator(this.user);
|
||||
}
|
||||
public isAdmin(): boolean {
|
||||
return Session.isPortalAdministrator(this.user);
|
||||
}
|
||||
changeTab(tab:"all" | "templates" | "profiles"){
|
||||
|
|
|
@ -135,7 +135,7 @@
|
|||
[class.uk-width-1-3@m]="indicator.width === 'small'"
|
||||
[class.uk-width-1-2@m]="indicator.width === 'medium'"
|
||||
[class.uk-width-1-1]="indicator.width === 'large'" class=" uk-margin-bottom">
|
||||
<div class="uk-card uk-card-default" [attr.uk-tooltip]="indicator.description"
|
||||
<div class="uk-card uk-card-default"
|
||||
[class.uk-disabled]="indicator.indicatorPaths[0].filtersApplied < countSelectedFilters()"
|
||||
[class.semiFiltered]="indicator.indicatorPaths[0].filtersApplied < countSelectedFilters()">
|
||||
<div class="uk-card-body uk-text-center">
|
||||
|
@ -147,7 +147,7 @@
|
|||
<h3 *ngIf="numberResults.get(i + '-' + j)" class="uk-margin-medium-top uk-text-bold">
|
||||
<span>{{numberResults.get(i + '-' + j) | number}}</span>
|
||||
</h3>
|
||||
|
||||
<ng-container *ngTemplateOutlet="description; context: {indicator:indicator}"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -173,10 +173,7 @@
|
|||
<div class="uk-card uk-card-default"
|
||||
[class.uk-disabled]="chartsActiveType.get(i + '-' + j).filtersApplied < countSelectedFilters()"
|
||||
[class.semiFiltered]="chartsActiveType.get(i + '-' + j).filtersApplied < countSelectedFilters()"
|
||||
><!--[
|
||||
attr
|
||||
.uk-tooltip]="indicator
|
||||
.description">-->
|
||||
>
|
||||
<div class="uk-card-body uk-text-center">
|
||||
<div *ngIf="indicator.indicatorPaths.length > 1" class="uk-button-group">
|
||||
<button *ngFor="let indicatorPath of indicator.indicatorPaths;"
|
||||
|
@ -196,7 +193,7 @@
|
|||
<img *ngIf="chartsActiveType.get(i + '-' + j).source === 'image'"
|
||||
[src]="chartsActiveType.get(i + '-' + j).safeResourceUrl"
|
||||
class="uk-width-1-1 uk-height-medium">
|
||||
<div class="uk-text-small uk-text-muted">{{indicator.description}}</div>
|
||||
<ng-container *ngTemplateOutlet="description; context: {indicator:indicator}"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -267,4 +264,14 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<ng-template #description let-indicator="indicator">
|
||||
<span class="descriptionIcon"
|
||||
*ngIf="(indicator.description && indicator.description.length > 0)
|
||||
|| (indicator.additionalDescription && indicator.additionalDescription.length > 0)"
|
||||
uk-icon="info"
|
||||
[attr.uk-tooltip]="'title:<div class=\'uk-padding-small\'>'+
|
||||
(indicator.description&& indicator.description.length > 0?indicator.description:'') +'<br>'+
|
||||
(indicator.additionalDescription && indicator.additionalDescription.length?indicator.additionalDescription:'')
|
||||
+'</div>'">
|
||||
</span>
|
||||
</ng-template>
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
[class.disable-sortable]="!canReorder"
|
||||
[class.uk-sortable-nodrag]="!canReorder">
|
||||
<div class="uk-card uk-card-default" [class.md-card-hover]="canReorder">
|
||||
<div class="uk-padding-small ">
|
||||
<div class="uk-padding-small uk-padding-remove-bottom ">
|
||||
<ng-container *ngTemplateOutlet="visibilityOptions; context:{indicator: indicator}"></ng-container>
|
||||
<div class="md-card-toolbar-actions uk-float-right" >
|
||||
|
||||
|
@ -118,14 +118,17 @@
|
|||
</div>
|
||||
|
||||
</div>
|
||||
<div class="">
|
||||
<div class="uk-flex uk-flex-center" uk-grid>
|
||||
<div class="">
|
||||
<div class=" uk-padding-small uk-padding-remove-top">
|
||||
<div class="">
|
||||
<div class="uk-text-center">
|
||||
{{indicator.name ? indicator.name : 'No title available'}}
|
||||
</div>
|
||||
<div class="uk-width-1-1">
|
||||
{{indicator.description}}
|
||||
</div>
|
||||
<div class="uk-width-1-1">
|
||||
{{indicator.additionalDescription}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -250,6 +253,9 @@
|
|||
<div class="uk-width-1-1 uk-text-muted uk-text-small">
|
||||
{{indicator.description ? indicator.description : ''}}
|
||||
</div>
|
||||
<div class="uk-width-1-1 uk-text-muted uk-text-small">
|
||||
{{indicator.additionalDescription ? indicator.additionalDescription : ''}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -290,9 +296,12 @@
|
|||
[okDisabled]="numberIndicatorFb && (numberIndicatorFb.invalid || numberIndicatorFb.pristine)">
|
||||
<div *ngIf="numberIndicatorFb" class="uk-padding-small" [formGroup]="numberIndicatorFb">
|
||||
<div dashboard-input class="uk-form-row" [formInput]="numberIndicatorFb.get('name')" label="Title"></div>
|
||||
<div dashboard-input class="uk-form-row" [formInput]="numberIndicatorFb.get('description')"
|
||||
<div *ngIf="isAdministrator" dashboard-input class="uk-form-row" [formInput]="numberIndicatorFb.get('description')"
|
||||
label="Description" type="textarea">
|
||||
</div>
|
||||
<div dashboard-input class="uk-form-row" [formInput]="numberIndicatorFb.get('additionalDescription')"
|
||||
label="Additional information" type="textarea">
|
||||
</div>
|
||||
<div class="uk-form-row uk-flex uk-flex-middle">
|
||||
<div dashboard-input class="uk-width-small" [formInput]="numberIndicatorFb.get('visibility')"
|
||||
label="Visibility" [options]="stakeholderUtils.visibility" type="select">
|
||||
|
@ -352,9 +361,12 @@
|
|||
[okDisabled]="chartIndicatorFb && (chartIndicatorFb.invalid || chartIndicatorFb.pristine)">
|
||||
<div *ngIf="chartIndicatorFb" class="uk-padding-small" [formGroup]="chartIndicatorFb">
|
||||
<div dashboard-input class="uk-form-row" [formInput]="chartIndicatorFb.get('name')" label="Title"></div>
|
||||
<div dashboard-input class="uk-form-row" [formInput]="chartIndicatorFb.get('description')"
|
||||
<div *ngIf="isAdministrator" dashboard-input class="uk-form-row" [formInput]="chartIndicatorFb.get('description')"
|
||||
label="Description" type="textarea">
|
||||
</div>
|
||||
<div dashboard-input class="uk-form-row" [formInput]="chartIndicatorFb.get('additionalDescription')"
|
||||
label="Additional information" type="textarea">
|
||||
</div>
|
||||
<div class="uk-form-row uk-flex uk-flex-middle">
|
||||
<div dashboard-input class="uk-width-small" [formInput]="chartIndicatorFb.get('visibility')"
|
||||
label="Visibility" [options]="stakeholderUtils.visibility" type="select">
|
||||
|
|
|
@ -321,6 +321,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
|
|||
sections.forEach(section =>
|
||||
section.indicators = section.indicators.filter(indicator => (indicator.name && indicator.name.toLowerCase().includes(value.toLowerCase()))
|
||||
|| (indicator.description && indicator.description.toLowerCase().includes(value.toLowerCase()))
|
||||
|| (indicator.additionalDescription && indicator.additionalDescription.toLowerCase().includes(value.toLowerCase()))
|
||||
|| indicator.indicatorPaths.filter(indicatorPath => (indicatorPath.parameters && indicatorPath.parameters.title &&
|
||||
indicatorPath.parameters.title.includes(value.toLowerCase()))).length > 0));
|
||||
}
|
||||
|
@ -567,6 +568,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
|
|||
_id: this.fb.control(this.indicator._id),
|
||||
name: this.fb.control(this.indicator.name, Validators.required),
|
||||
description: this.fb.control(this.indicator.description),
|
||||
additionalDescription: this.fb.control(this.indicator.additionalDescription),
|
||||
visibility: this.fb.control(this.indicator.visibility),
|
||||
indicatorPaths: this.fb.array([], Validators.required),
|
||||
type: this.fb.control(this.indicator.type),
|
||||
|
@ -577,11 +579,12 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
|
|||
this.addNumberIndicatorPath(this.statisticsService.getNumberUrl(indicatorPath.source,this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)),indicatorPath.parameters, indicatorPath.source, this.getJsonPathAsFormArray(indicatorPath));
|
||||
});
|
||||
} else {
|
||||
this.indicator = new Indicator('', '', 'number', 'small', "PUBLIC", []);
|
||||
this.indicator = new Indicator('', '', '','number', 'small', "PUBLIC", []);
|
||||
this.numberIndicatorFb = this.fb.group({
|
||||
_id: this.fb.control(this.indicator._id),
|
||||
name: this.fb.control(this.indicator.name, Validators.required),
|
||||
description: this.fb.control(this.indicator.description),
|
||||
additionalDescription: this.fb.control(this.indicator.additionalDescription),
|
||||
visibility: this.fb.control(this.indicator.visibility),
|
||||
indicatorPaths: this.fb.array([], Validators.required),
|
||||
type: this.fb.control(this.indicator.type),
|
||||
|
@ -618,6 +621,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
|
|||
_id: this.fb.control(this.indicator._id),
|
||||
name: this.fb.control(this.indicator.name),
|
||||
description: this.fb.control(this.indicator.description),
|
||||
additionalDescription: this.fb.control(this.indicator.additionalDescription),
|
||||
visibility: this.fb.control(this.indicator.visibility),
|
||||
indicatorPaths: this.fb.array([]),
|
||||
width: this.fb.control(this.indicator.width),
|
||||
|
@ -629,11 +633,12 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
|
|||
indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
|
||||
});
|
||||
} else {
|
||||
this.indicator = new Indicator('', '', 'chart', 'medium', "PUBLIC",[]);
|
||||
this.indicator = new Indicator('', '', '', 'chart', 'medium', "PUBLIC",[]);
|
||||
this.chartIndicatorFb = this.fb.group({
|
||||
_id: this.fb.control(this.indicator._id),
|
||||
name: this.fb.control(this.indicator.name),
|
||||
description: this.fb.control(this.indicator.description),
|
||||
additionalDescription: this.fb.control(this.indicator.additionalDescription),
|
||||
visibility: this.fb.control(this.indicator.visibility),
|
||||
indicatorPaths: this.fb.array([]),
|
||||
width: this.fb.control(this.indicator.width, Validators.required),
|
||||
|
@ -736,8 +741,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
|
|||
}
|
||||
|
||||
public get isAdministrator(): boolean {
|
||||
return this.properties.environment == 'development' || Session.isPortalAdministrator(this.user);
|
||||
// return Session.isPortalAdministrator(this.user) || Session.isMonitorCurator(this.user) || Session.isCommunityCurator(this.user)
|
||||
return Session.isPortalAdministrator(this.user) || Session.isMonitorCurator(this.user) || Session.isCommunityCurator(this.user)
|
||||
}
|
||||
|
||||
refreshIndicator() {
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
class="uk-margin-top uk-margin-small-right uk-visible-toggle"
|
||||
[class.uk-active]="topicIndex == i"
|
||||
[class.uk-text-bold]="topicIndex == i">
|
||||
<a [routerLink]="'../' + topic.alias" class="uk-flex">
|
||||
<a [routerLink]="'/admin/'+stakeholder.alias + '/indicators/' + topic.alias" class="uk-flex">
|
||||
<span *ngIf="topic.icon" class="menu_icon">
|
||||
<!-- <span [innerHTML]="satinizeHTML(topic.icon)"></span>-->
|
||||
</span>
|
||||
|
|
|
@ -16,13 +16,9 @@ export class AdminDashboardGuard implements CanActivate, CanLoad {
|
|||
}
|
||||
|
||||
check(path: string): Observable<boolean> | boolean {
|
||||
/* //Argiro testing
|
||||
if(properties.environment == "development"){
|
||||
return true;
|
||||
}*/
|
||||
if (Session.isLoggedIn()) {
|
||||
const obs = this.userManagementService.getUserInfo(false).pipe(map(user => {
|
||||
return (Session.isPortalAdministrator(user) || Session.isCommunityCurator(user) || Session.isMonitorCurator(user));
|
||||
return (Session.isPortalAdministrator(user) || Session.isCommunityCurator(user) || Session.isMonitorCurator(user) || Session.isKindOfMonitorManager(user));
|
||||
}));
|
||||
obs.pipe(filter(isLoggedIn => !isLoggedIn)).subscribe(() => {
|
||||
this.router.navigate(['/user-info'], {queryParams: {'errorCode': LoginErrorCodes.NOT_ADMIN, 'redirectUrl':path}});
|
||||
|
|
|
@ -506,7 +506,7 @@ export class IndicatorUtils {
|
|||
return values.length > 1;
|
||||
}
|
||||
generateIndicatorByForm(form: any, indicatorPaths: IndicatorPath[], type:IndicatorType, addParameters:boolean = true ): Indicator {
|
||||
let indicator: Indicator = new Indicator(form.name, form.description, type,
|
||||
let indicator: Indicator = new Indicator(form.name, form.description, form.additionalDescription, type,
|
||||
form.width, form.visibility, indicatorPaths, form.defaultId);
|
||||
indicator._id = form._id;
|
||||
form.indicatorPaths.forEach((indicatorPath, index) => {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
.stakeholderPage {
|
||||
--primary-color: var(--theme-secondary-color);
|
||||
--secondary-color: var(--theme-primary-color);
|
||||
--secondary-color: var(--theme-secondary-color);
|
||||
}
|
||||
/*Stakeholder Specific*/
|
||||
.publicationsSearchForm{
|
||||
|
@ -302,3 +302,8 @@ li.selectedVisibility::before {
|
|||
padding: 0px 2px 3px 0px;
|
||||
|
||||
}
|
||||
.descriptionIcon{
|
||||
position: absolute;
|
||||
bottom: 3px;
|
||||
left: 3px;
|
||||
}
|
||||
|
|
|
@ -49,13 +49,13 @@ export let properties: EnvProperties = {
|
|||
vocabulariesAPI: "https://dev-openaire.d4science.org/provision/mvc/vocabularies/",
|
||||
piwikBaseUrl: "https://analytics.openaire.eu/piwik.php?idsite=",
|
||||
piwikSiteId: "80",
|
||||
/*registryUrl: 'http://mpagasas.di.uoa.gr:8080/dnet-openaire-users-1.0.0-SNAPSHOT/api/registry/',
|
||||
registryUrl: 'http://mpagasas.di.uoa.gr:8080/dnet-openaire-users-1.0.0-SNAPSHOT/api/registry/',
|
||||
loginUrl: "http://mpagasas.di.uoa.gr:8180/dnet-login/openid_connect_login",
|
||||
userInfoUrl: "http://mpagasas.di.uoa.gr:8080/dnet-openaire-users-1.0.0-SNAPSHOT/api/users/getUserInfo?accessToken=",
|
||||
logoutUrl: 'http://mpagasas.di.uoa.gr:8180/dnet-login/openid_logout',*/
|
||||
loginUrl: "http://dl170.madgik.di.uoa.gr:8180/dnet-login/openid_connect_login",
|
||||
logoutUrl: 'http://mpagasas.di.uoa.gr:8180/dnet-login/openid_logout',
|
||||
/* loginUrl: "http://dl170.madgik.di.uoa.gr:8180/dnet-login/openid_connect_login",
|
||||
userInfoUrl: "http://dl170.madgik.di.uoa.gr:8180/dnet-openaire-users-1.0.0-SNAPSHOT/api/users/getUserInfo?accessToken=",
|
||||
logoutUrl: "https://aai.openaire.eu/proxy/saml2/idp/SingleLogoutService.php?ReturnTo=",
|
||||
logoutUrl: "https://aai.openaire.eu/proxy/saml2/idp/SingleLogoutService.php?ReturnTo=",*/
|
||||
cookieDomain: ".di.uoa.gr",
|
||||
feedbackmail: "openaire.test@gmail.com",
|
||||
cacheUrl: "http://scoobydoo.di.uoa.gr:3000/get?url=",
|
||||
|
|
Loading…
Reference in New Issue