initial commit
|
|
@ -0,0 +1,4 @@
|
|||
.gradle/
|
||||
.sass-cache/
|
||||
build/
|
||||
target/
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
# Changelog for My VREs Portlet
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [v2.6.1] - 2023-03-31
|
||||
|
||||
- minor fix for pom dependency
|
||||
|
||||
## [v2.6.0] - 2019-09-17
|
||||
|
||||
- It provides part of the gcube oAuth2 service with capability to be deployed on a multi instance cluster, it no longer posts the temporary code to the oAuth service, it places it in the memcahced cluster.
|
||||
|
||||
## [v2.3.0] - 2017-11-12
|
||||
|
||||
- Adapted so support VRE selection upon authorisation requests
|
||||
|
||||
## [v2.2.0] - 2016-11-25
|
||||
|
||||
- removed ASL Session
|
||||
- Implemented Feature #4877 remove VRE association to single Category constraint
|
||||
|
||||
## [v2.1.0] - 2016-09-12
|
||||
|
||||
- Shows only the virtual groups available in the Site it is deployed on
|
||||
- VRE was not updated in the portlet due to liferay versioning of Document Library
|
||||
|
||||
## [v2.0.1] - 2016-04-02
|
||||
|
||||
- Ported to Liferay 6.2
|
||||
|
||||
## [v1.0.0] -2013-10-21
|
||||
|
||||
- First release
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
# Acknowledgments
|
||||
|
||||
The projects leading to this software have received funding from a series of European Union programmes including:
|
||||
|
||||
- the Sixth Framework Programme for Research and Technological Development
|
||||
- [DILIGENT](https://cordis.europa.eu/project/id/004260) (grant no. 004260).
|
||||
- the Seventh Framework Programme for research, technological development and demonstration
|
||||
- [D4Science](https://cordis.europa.eu/project/id/212488) (grant no. 212488);
|
||||
- [D4Science-II](https://cordis.europa.eu/project/id/239019) (grant no.239019);
|
||||
- [ENVRI](https://cordis.europa.eu/project/id/283465) (grant no. 283465);
|
||||
- [iMarine](https://cordis.europa.eu/project/id/283644) (grant no. 283644);
|
||||
- [EUBrazilOpenBio](https://cordis.europa.eu/project/id/288754) (grant no. 288754).
|
||||
- the H2020 research and innovation programme
|
||||
- [SoBigData](https://cordis.europa.eu/project/id/654024) (grant no. 654024);
|
||||
- [PARTHENOS](https://cordis.europa.eu/project/id/654119) (grant no. 654119);
|
||||
- [EGI-Engage](https://cordis.europa.eu/project/id/654142) (grant no. 654142);
|
||||
- [ENVRI PLUS](https://cordis.europa.eu/project/id/654182) (grant no. 654182);
|
||||
- [BlueBRIDGE](https://cordis.europa.eu/project/id/675680) (grant no. 675680);
|
||||
- [PerformFISH](https://cordis.europa.eu/project/id/727610) (grant no. 727610);
|
||||
- [AGINFRA PLUS](https://cordis.europa.eu/project/id/731001) (grant no. 731001);
|
||||
- [DESIRA](https://cordis.europa.eu/project/id/818194) (grant no. 818194);
|
||||
- [ARIADNEplus](https://cordis.europa.eu/project/id/823914) (grant no. 823914);
|
||||
- [RISIS 2](https://cordis.europa.eu/project/id/824091) (grant no. 824091);
|
||||
- [EOSC-Pillar](https://cordis.europa.eu/project/id/857650) (grant no. 857650);
|
||||
- [Blue Cloud](https://cordis.europa.eu/project/id/862409) (grant no. 862409);
|
||||
- [SoBigData-PlusPlus](https://cordis.europa.eu/project/id/871042) (grant no. 871042);
|
||||
|
|
@ -0,0 +1,311 @@
|
|||
#European Union Public Licence V.1.1
|
||||
|
||||
##*EUPL © the European Community 2007*
|
||||
|
||||
|
||||
This **European Union Public Licence** (the **“EUPL”**) applies to the Work or Software
|
||||
(as defined below) which is provided under the terms of this Licence. Any use of
|
||||
the Work, other than as authorised under this Licence is prohibited (to the
|
||||
extent such use is covered by a right of the copyright holder of the Work).
|
||||
|
||||
The Original Work is provided under the terms of this Licence when the Licensor
|
||||
(as defined below) has placed the following notice immediately following the
|
||||
copyright notice for the Original Work:
|
||||
|
||||
**Licensed under the EUPL V.1.1**
|
||||
|
||||
or has expressed by any other mean his willingness to license under the EUPL.
|
||||
|
||||
|
||||
|
||||
##1. Definitions
|
||||
|
||||
In this Licence, the following terms have the following meaning:
|
||||
|
||||
- The Licence: this Licence.
|
||||
|
||||
- The Original Work or the Software: the software distributed and/or
|
||||
communicated by the Licensor under this Licence, available as Source Code and
|
||||
also as Executable Code as the case may be.
|
||||
|
||||
- Derivative Works: the works or software that could be created by the Licensee,
|
||||
based upon the Original Work or modifications thereof. This Licence does not
|
||||
define the extent of modification or dependence on the Original Work required
|
||||
in order to classify a work as a Derivative Work; this extent is determined by
|
||||
copyright law applicable in the country mentioned in Article 15.
|
||||
|
||||
- The Work: the Original Work and/or its Derivative Works.
|
||||
|
||||
- The Source Code: the human-readable form of the Work which is the most
|
||||
convenient for people to study and modify.
|
||||
|
||||
- The Executable Code: any code which has generally been compiled and which is
|
||||
meant to be interpreted by a computer as a program.
|
||||
|
||||
- The Licensor: the natural or legal person that distributes and/or communicates
|
||||
the Work under the Licence.
|
||||
|
||||
- Contributor(s): any natural or legal person who modifies the Work under the
|
||||
Licence, or otherwise contributes to the creation of a Derivative Work.
|
||||
|
||||
- The Licensee or “You”: any natural or legal person who makes any usage of the
|
||||
Software under the terms of the Licence.
|
||||
|
||||
- Distribution and/or Communication: any act of selling, giving, lending,
|
||||
renting, distributing, communicating, transmitting, or otherwise making
|
||||
available, on-line or off-line, copies of the Work or providing access to its
|
||||
essential functionalities at the disposal of any other natural or legal
|
||||
person.
|
||||
|
||||
|
||||
|
||||
##2. Scope of the rights granted by the Licence
|
||||
|
||||
The Licensor hereby grants You a world-wide, royalty-free, non-exclusive,
|
||||
sub-licensable licence to do the following, for the duration of copyright vested
|
||||
in the Original Work:
|
||||
|
||||
- use the Work in any circumstance and for all usage, reproduce the Work, modify
|
||||
- the Original Work, and make Derivative Works based upon the Work, communicate
|
||||
- to the public, including the right to make available or display the Work or
|
||||
- copies thereof to the public and perform publicly, as the case may be, the
|
||||
- Work, distribute the Work or copies thereof, lend and rent the Work or copies
|
||||
- thereof, sub-license rights in the Work or copies thereof.
|
||||
|
||||
Those rights can be exercised on any media, supports and formats, whether now
|
||||
known or later invented, as far as the applicable law permits so.
|
||||
|
||||
In the countries where moral rights apply, the Licensor waives his right to
|
||||
exercise his moral right to the extent allowed by law in order to make effective
|
||||
the licence of the economic rights here above listed.
|
||||
|
||||
The Licensor grants to the Licensee royalty-free, non exclusive usage rights to
|
||||
any patents held by the Licensor, to the extent necessary to make use of the
|
||||
rights granted on the Work under this Licence.
|
||||
|
||||
|
||||
|
||||
##3. Communication of the Source Code
|
||||
|
||||
The Licensor may provide the Work either in its Source Code form, or as
|
||||
Executable Code. If the Work is provided as Executable Code, the Licensor
|
||||
provides in addition a machine-readable copy of the Source Code of the Work
|
||||
along with each copy of the Work that the Licensor distributes or indicates, in
|
||||
a notice following the copyright notice attached to the Work, a repository where
|
||||
the Source Code is easily and freely accessible for as long as the Licensor
|
||||
continues to distribute and/or communicate the Work.
|
||||
|
||||
|
||||
|
||||
##4. Limitations on copyright
|
||||
|
||||
Nothing in this Licence is intended to deprive the Licensee of the benefits from
|
||||
any exception or limitation to the exclusive rights of the rights owners in the
|
||||
Original Work or Software, of the exhaustion of those rights or of other
|
||||
applicable limitations thereto.
|
||||
|
||||
|
||||
|
||||
##5. Obligations of the Licensee
|
||||
|
||||
The grant of the rights mentioned above is subject to some restrictions and
|
||||
obligations imposed on the Licensee. Those obligations are the following:
|
||||
|
||||
Attribution right: the Licensee shall keep intact all copyright, patent or
|
||||
trademarks notices and all notices that refer to the Licence and to the
|
||||
disclaimer of warranties. The Licensee must include a copy of such notices and a
|
||||
copy of the Licence with every copy of the Work he/she distributes and/or
|
||||
communicates. The Licensee must cause any Derivative Work to carry prominent
|
||||
notices stating that the Work has been modified and the date of modification.
|
||||
|
||||
Copyleft clause: If the Licensee distributes and/or communicates copies of the
|
||||
Original Works or Derivative Works based upon the Original Work, this
|
||||
Distribution and/or Communication will be done under the terms of this Licence
|
||||
or of a later version of this Licence unless the Original Work is expressly
|
||||
distributed only under this version of the Licence. The Licensee (becoming
|
||||
Licensor) cannot offer or impose any additional terms or conditions on the Work
|
||||
or Derivative Work that alter or restrict the terms of the Licence.
|
||||
|
||||
Compatibility clause: If the Licensee Distributes and/or Communicates Derivative
|
||||
Works or copies thereof based upon both the Original Work and another work
|
||||
licensed under a Compatible Licence, this Distribution and/or Communication can
|
||||
be done under the terms of this Compatible Licence. For the sake of this clause,
|
||||
“Compatible Licence” refers to the licences listed in the appendix attached to
|
||||
this Licence. Should the Licensee’s obligations under the Compatible Licence
|
||||
conflict with his/her obligations under this Licence, the obligations of the
|
||||
Compatible Licence shall prevail.
|
||||
|
||||
Provision of Source Code: When distributing and/or communicating copies of the
|
||||
Work, the Licensee will provide a machine-readable copy of the Source Code or
|
||||
indicate a repository where this Source will be easily and freely available for
|
||||
as long as the Licensee continues to distribute and/or communicate the Work.
|
||||
|
||||
Legal Protection: This Licence does not grant permission to use the trade names,
|
||||
trademarks, service marks, or names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the copyright notice.
|
||||
|
||||
|
||||
|
||||
##6. Chain of Authorship
|
||||
|
||||
The original Licensor warrants that the copyright in the Original Work granted
|
||||
hereunder is owned by him/her or licensed to him/her and that he/she has the
|
||||
power and authority to grant the Licence.
|
||||
|
||||
Each Contributor warrants that the copyright in the modifications he/she brings
|
||||
to the Work are owned by him/her or licensed to him/her and that he/she has the
|
||||
power and authority to grant the Licence.
|
||||
|
||||
Each time You accept the Licence, the original Licensor and subsequent
|
||||
Contributors grant You a licence to their contributions to the Work, under the
|
||||
terms of this Licence.
|
||||
|
||||
|
||||
|
||||
##7. Disclaimer of Warranty
|
||||
|
||||
The Work is a work in progress, which is continuously improved by numerous
|
||||
contributors. It is not a finished work and may therefore contain defects or
|
||||
“bugs” inherent to this type of software development.
|
||||
|
||||
For the above reason, the Work is provided under the Licence on an “as is” basis
|
||||
and without warranties of any kind concerning the Work, including without
|
||||
limitation merchantability, fitness for a particular purpose, absence of defects
|
||||
or errors, accuracy, non-infringement of intellectual property rights other than
|
||||
copyright as stated in Article 6 of this Licence.
|
||||
|
||||
This disclaimer of warranty is an essential part of the Licence and a condition
|
||||
for the grant of any rights to the Work.
|
||||
|
||||
|
||||
|
||||
##8. Disclaimer of Liability
|
||||
|
||||
Except in the cases of wilful misconduct or damages directly caused to natural
|
||||
persons, the Licensor will in no event be liable for any direct or indirect,
|
||||
material or moral, damages of any kind, arising out of the Licence or of the use
|
||||
of the Work, including without limitation, damages for loss of goodwill, work
|
||||
stoppage, computer failure or malfunction, loss of data or any commercial
|
||||
damage, even if the Licensor has been advised of the possibility of such
|
||||
damage. However, the Licensor will be liable under statutory product liability
|
||||
laws as far such laws apply to the Work.
|
||||
|
||||
|
||||
|
||||
##9. Additional agreements
|
||||
|
||||
While distributing the Original Work or Derivative Works, You may choose to
|
||||
conclude an additional agreement to offer, and charge a fee for, acceptance of
|
||||
support, warranty, indemnity, or other liability obligations and/or services
|
||||
consistent with this Licence. However, in accepting such obligations, You may
|
||||
act only on your own behalf and on your sole responsibility, not on behalf of
|
||||
the original Licensor or any other Contributor, and only if You agree to
|
||||
indemnify, defend, and hold each Contributor harmless for any liability incurred
|
||||
by, or claims asserted against such Contributor by the fact You have accepted
|
||||
any such warranty or additional liability.
|
||||
|
||||
|
||||
|
||||
##10. Acceptance of the Licence
|
||||
|
||||
The provisions of this Licence can be accepted by clicking on an icon “I agree”
|
||||
placed under the bottom of a window displaying the text of this Licence or by
|
||||
affirming consent in any other similar way, in accordance with the rules of
|
||||
applicable law. Clicking on that icon indicates your clear and irrevocable
|
||||
acceptance of this Licence and all of its terms and conditions.
|
||||
|
||||
Similarly, you irrevocably accept this Licence and all of its terms and
|
||||
conditions by exercising any rights granted to You by Article 2 of this Licence,
|
||||
such as the use of the Work, the creation by You of a Derivative Work or the
|
||||
Distribution and/or Communication by You of the Work or copies thereof.
|
||||
|
||||
|
||||
|
||||
##11. Information to the public
|
||||
|
||||
In case of any Distribution and/or Communication of the Work by means of
|
||||
electronic communication by You (for example, by offering to download the Work
|
||||
from a remote location) the distribution channel or media (for example, a
|
||||
website) must at least provide to the public the information requested by the
|
||||
applicable law regarding the Licensor, the Licence and the way it may be
|
||||
accessible, concluded, stored and reproduced by the Licensee.
|
||||
|
||||
|
||||
|
||||
##12. Termination of the Licence
|
||||
|
||||
The Licence and the rights granted hereunder will terminate automatically upon
|
||||
any breach by the Licensee of the terms of the Licence.
|
||||
|
||||
Such a termination will not terminate the licences of any person who has
|
||||
received the Work from the Licensee under the Licence, provided such persons
|
||||
remain in full compliance with the Licence.
|
||||
|
||||
|
||||
|
||||
##13. Miscellaneous
|
||||
|
||||
Without prejudice of Article 9 above, the Licence represents the complete
|
||||
agreement between the Parties as to the Work licensed hereunder.
|
||||
|
||||
If any provision of the Licence is invalid or unenforceable under applicable
|
||||
law, this will not affect the validity or enforceability of the Licence as a
|
||||
whole. Such provision will be construed and/or reformed so as necessary to make
|
||||
it valid and enforceable.
|
||||
|
||||
The European Commission may publish other linguistic versions and/or new
|
||||
versions of this Licence, so far this is required and reasonable, without
|
||||
reducing the scope of the rights granted by the Licence. New versions of the
|
||||
Licence will be published with a unique version number.
|
||||
|
||||
All linguistic versions of this Licence, approved by the European Commission,
|
||||
have identical value. Parties can take advantage of the linguistic version of
|
||||
their choice.
|
||||
|
||||
|
||||
|
||||
##14. Jurisdiction
|
||||
|
||||
Any litigation resulting from the interpretation of this License, arising
|
||||
between the European Commission, as a Licensor, and any Licensee, will be
|
||||
subject to the jurisdiction of the Court of Justice of the European Communities,
|
||||
as laid down in article 238 of the Treaty establishing the European Community.
|
||||
|
||||
Any litigation arising between Parties, other than the European Commission, and
|
||||
resulting from the interpretation of this License, will be subject to the
|
||||
exclusive jurisdiction of the competent court where the Licensor resides or
|
||||
conducts its primary business.
|
||||
|
||||
|
||||
|
||||
##15. Applicable Law
|
||||
|
||||
This Licence shall be governed by the law of the European Union country where
|
||||
the Licensor resides or has his registered office.
|
||||
|
||||
This licence shall be governed by the Belgian law if:
|
||||
|
||||
- a litigation arises between the European Commission, as a Licensor, and any
|
||||
- Licensee; the Licensor, other than the European Commission, has no residence
|
||||
- or registered office inside a European Union country.
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
##Appendix
|
||||
|
||||
|
||||
**“Compatible Licences”** according to article 5 EUPL are:
|
||||
|
||||
|
||||
- GNU General Public License (GNU GPL) v. 2
|
||||
|
||||
- Open Software License (OSL) v. 2.1, v. 3.0
|
||||
|
||||
- Common Public License v. 1.0
|
||||
|
||||
- Eclipse Public License v. 1.0
|
||||
|
||||
- Cecill v. 2.0
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
# Porting "MyVres" (Liferay 7.4)
|
||||
|
||||
Questo documento descrive il porting della componente "MyVres" a Liferay 7.4 e, in particolare, come vengono recuperate e mostrate le VRE associate all’utente.
|
||||
|
||||
## Obiettivo
|
||||
- Sostituire la vecchia logica GWT/RPC con un `MVCResourceCommand` che restituisce JSON.
|
||||
- Allineare la selezione delle VRE all’utente e al sito corrente, mantenendo il comportamento di Liferay 6.2 (URL, categorie, membership).
|
||||
|
||||
## File principali
|
||||
- `src/main/java/org/gcube/portlet/user/my_vres/resource/GetUserVREsMVCResourceCommand.java`
|
||||
|
||||
## Modifiche chiave
|
||||
- Introduzione di un `MVCResourceCommand` esposto come `mvc.command.name=/myvres/get_user_vres` che produce i dati per la UI.
|
||||
- Recupero affidabile di `siteGroupId` e `userId` via `ThemeDisplay` con fallback se non disponibile.
|
||||
- Popolamento diretto delle VRE dell’utente sul sito corrente, evitando dipendenze dalla gerarchia VO.
|
||||
- Categorizzazione per Virtual Group del sito corrente.
|
||||
- Risoluzione dei nomi localizzati (XML Liferay) per mostrare solo il testo della lingua corrente o del default locale.
|
||||
- Mantenimento del formato di `friendlyURL` stile 6.2 (senza `/workspace`).
|
||||
- Validazione CSRF tramite `AuthTokenUtil.checkCSRFToken`.
|
||||
|
||||
## Recupero di sito e utente
|
||||
- `ThemeDisplay td = (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY)`
|
||||
- `siteGroupId = td.getSiteGroupId()`
|
||||
- `userId = td.getUserId()`
|
||||
- Fallback:
|
||||
- `siteGroupId = ManagementUtils.getSiteGroupIdFromServletRequest(serverName)`
|
||||
- `userId = PortalUtil.getUserId(request)`
|
||||
|
||||
## Come vengono prese le VRE associate all’utente
|
||||
1. Si interroga il `GroupManager` per ottenere le VRE dell’utente limitate al sito corrente:
|
||||
- `Set<GCubeGroup> userSiteGroups = gm.listGroupsByUserAndSiteGroupId(userId, siteGroupId)`
|
||||
2. Se il set è vuoto (es. mismatch dei Virtual Group), si applica un fallback alle VRE generali dell’utente:
|
||||
- `Collection<GCubeGroup> sourceVREs = gm.listVresByUser(userId)`
|
||||
3. Per ciascuna VRE:
|
||||
- `name`: risolto in modo localizzato (vedi sotto).
|
||||
- `context`: `gm.getInfrastructureScope(vre.getGroupId())`.
|
||||
- `imageURL`: `gm.getGroupLogoURL(vre.getLogoId())`.
|
||||
- `friendlyURL`: `"/group" + vre.getFriendlyURL()` (senza `/workspace`).
|
||||
- `userBelonging`: forzato a `BELONGING` poiché la VRE proviene dalle membership dell’utente.
|
||||
|
||||
## Categorizzazione per Virtual Group
|
||||
- Si leggono i `VirtualGroup` del sito corrente: `gm.getVirtualGroups(siteGroupId)`; ogni nome crea una categoria.
|
||||
- Ogni VRE viene associata alle categorie dei suoi `VirtualGroup`: `gm.getVirtualGroups(vre.getGroupId())`.
|
||||
|
||||
## Localizzazione dei nomi (no XML a schermo)
|
||||
- In Liferay, alcuni campi possono essere salvati come XML localizzato (con più lingue). Per evitare che appaia l’XML, durante la costruzione del JSON si risolve il testo nella lingua corrente o nel default locale, utilizzando:
|
||||
- `LocalizationUtil.getLocalization(xml, langId)`
|
||||
- Fallback: `LocalizationUtil.getDefaultLanguageId(xml)`
|
||||
- Questo è applicato sia ai nomi delle categorie (Virtual Group) che ai nomi delle VRE.
|
||||
|
||||
## Sicurezza
|
||||
- Validazione CSRF: `AuthTokenUtil.checkCSRFToken(httpServletRequest, GetUserVREsMVCResourceCommand.class.getName())`.
|
||||
|
||||
## Struttura JSON restituita
|
||||
Endpoint: `/myvres/get_user_vres`
|
||||
|
||||
```json
|
||||
{
|
||||
"categories": [
|
||||
{
|
||||
"name": "Virtual Group A",
|
||||
"vres": [
|
||||
{
|
||||
"name": "devVRE",
|
||||
"description": "",
|
||||
"imageURL": "...",
|
||||
"context": "/gcube/dev",
|
||||
"friendlyURL": "/group/devvre",
|
||||
"userBelonging": "BELONGING"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Note sul porting da 6.2
|
||||
- URL delle VRE: mantenuto il formato `"/group" + friendlyURL` senza `/workspace` per compatibilità UI.
|
||||
- La gerarchia VO→VRE non è più necessaria per determinare la membership: si usano direttamente le API di membership per sito.
|
||||
- I Virtual Group sono letti come attributi/custom group del sito corrente (7.4).
|
||||
|
||||
## Deploy e verifica
|
||||
- Deploy: `./gradlew :modules:my-vres-portlet:deploy`
|
||||
- Anteprima: pagina del sito corrente che include la portlet; le VRE appaiono sotto i rispettivi Virtual Group. Le card aprono l’URL `"/group/<friendly>"`.
|
||||
|
||||
## Troubleshooting rapido
|
||||
- Se la lista è vuota: controllare che l’utente sia membro delle VRE nel sito corrente (Control Panel → Sites → Membership) e che i Virtual Group della VRE coincidano con quelli del sito.
|
||||
- In caso di mismatch, il fallback mostra comunque le VRE dell’utente fuori sito.
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
Bundle-Name: my-vres-portlet
|
||||
Bundle-SymbolicName: org.gcube.portlet.user.my_vres
|
||||
Bundle-Version: 1.0.0
|
||||
Export-Package: org.gcube.portlet.user.my_vres.constants
|
||||
Provide-Capability:\
|
||||
liferay.language.resources;\
|
||||
resource.bundle.base.name="content.Language"
|
||||
|
||||
# Import packages (avoid jakarta, prefer javax in this workspace)
|
||||
Import-Package: \
|
||||
!jakarta.servlet.*,\
|
||||
javax.servlet;version="[3.0,5)",\
|
||||
javax.servlet.http;version="[3.0,5)",\
|
||||
org.gcube.vomanagement.*,\
|
||||
*
|
||||
|
||||
# Include resources
|
||||
-includeresource: META-INF/resources=src/main/resources/META-INF/resources
|
||||
|
||||
# JSP Taglib requirements
|
||||
Require-Capability:\
|
||||
osgi.extender;\
|
||||
filter:="(&(osgi.extender=jsp.taglib)(uri=http://java.sun.com/portlet_2_0))",\
|
||||
osgi.extender;\
|
||||
filter:="(&(osgi.extender=jsp.taglib)(uri=http://liferay.com/tld/aui))",\
|
||||
osgi.extender;\
|
||||
filter:="(&(osgi.extender=jsp.taglib)(uri=http://liferay.com/tld/frontend))",\
|
||||
osgi.extender;\
|
||||
filter:="(&(osgi.extender=jsp.taglib)(uri=http://liferay.com/tld/portlet))",\
|
||||
osgi.extender;\
|
||||
filter:="(&(osgi.extender=jsp.taglib)(uri=http://liferay.com/tld/theme))",\
|
||||
osgi.extender;\
|
||||
filter:="(&(osgi.extender=jsp.taglib)(uri=http://liferay.com/tld/ui))",\
|
||||
osgi.extender;\
|
||||
filter:="(&(osgi.extender=jsp.taglib)(uri=http://liferay.com/tld/util))"
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
repositories {
|
||||
mavenCentral() // keep common repos
|
||||
maven {
|
||||
url "https://nexus.d4science.org/nexus/content/repositories/gcube-releases"
|
||||
}
|
||||
maven {
|
||||
url "https://nexus.d4science.org/nexus/content/repositories/gcube-snapshots"
|
||||
}
|
||||
maven {
|
||||
url "https://nexus.d4science.org/nexus/content/repositories/gcube-externals"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation platform("org.gcube.distribution:maven-portal-bom-javax:5.0.0-SNAPSHOT")
|
||||
|
||||
compileOnly group: "com.liferay.portal", name: "release.portal.api"
|
||||
compileOnly group: "com.liferay", name: "com.liferay.frontend.taglib", version: "7.0.0"
|
||||
compileOnly group: "javax.portlet", name: "portlet-api"
|
||||
compileOnly group: "javax.servlet", name: "javax.servlet-api"
|
||||
implementation group: "com.googlecode.json-simple", name: "json-simple", version: "1.1.1"
|
||||
implementation group: "org.apache.httpcomponents", name: "httpclient", version: "4.5.13"
|
||||
cssBuilder group: "com.liferay", name: "com.liferay.css.builder", version: "3.1.4"
|
||||
|
||||
// Local modules (if needed for shared services)
|
||||
implementation project(":modules:portal-manager")
|
||||
implementation project(":modules:usermanag")
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package org.gcube.portlet.user.my_vres.constants;
|
||||
|
||||
public class MyVresPortletKeys {
|
||||
public static final String MYVRES =
|
||||
"org_gcube_portlet_user_my_vres_MyVresPortlet";
|
||||
// Bundle symbolic name as declared in bnd.bnd
|
||||
public static final String BUNDLE_SYMBOLIC_NAME = "org.gcube.portlet.user.my_vres";
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package org.gcube.portlet.user.my_vres.portlet;
|
||||
|
||||
import org.gcube.portlet.user.my_vres.constants.MyVresPortletKeys;
|
||||
|
||||
import com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;
|
||||
|
||||
import javax.portlet.Portlet;
|
||||
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* @author netfarm-m2
|
||||
*/
|
||||
@Component(
|
||||
property = {
|
||||
"com.liferay.portlet.display-category=category.sample",
|
||||
"com.liferay.portlet.header-portlet-css=/css/main.css",
|
||||
"com.liferay.portlet.instanceable=true",
|
||||
"javax.portlet.display-name=MyVres",
|
||||
"javax.portlet.init-param.template-path=/",
|
||||
"javax.portlet.init-param.view-template=/view.jsp",
|
||||
"javax.portlet.name=" + MyVresPortletKeys.MYVRES,
|
||||
"javax.portlet.resource-bundle=content.Language",
|
||||
"javax.portlet.security-role-ref=power-user,user",
|
||||
// Opt-in to Portlet 3.0 to avoid 'Requires 3.0 opt-in' when using render parameters
|
||||
"javax.portlet.version=3.0"
|
||||
},
|
||||
service = Portlet.class
|
||||
)
|
||||
public class MyVresPortlet extends MVCPortlet {
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
package org.gcube.portlet.user.my_vres.resource;
|
||||
|
||||
import com.liferay.portal.kernel.json.JSONFactoryUtil;
|
||||
import com.liferay.portal.kernel.json.JSONArray;
|
||||
import com.liferay.portal.kernel.json.JSONObject;
|
||||
import com.liferay.portal.kernel.portlet.bridges.mvc.MVCResourceCommand;
|
||||
import com.liferay.portal.kernel.security.auth.AuthTokenUtil;
|
||||
import com.liferay.portal.kernel.security.auth.PrincipalException;
|
||||
import com.liferay.portal.kernel.theme.ThemeDisplay;
|
||||
import com.liferay.portal.kernel.util.ParamUtil;
|
||||
import com.liferay.portal.kernel.util.WebKeys;
|
||||
import com.liferay.portal.kernel.util.PortalUtil;
|
||||
import com.liferay.portal.kernel.util.LocalizationUtil;
|
||||
import com.liferay.portal.kernel.util.LocaleUtil;
|
||||
import com.liferay.portal.kernel.util.Validator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.portlet.PortletException;
|
||||
import javax.portlet.ResourceRequest;
|
||||
import javax.portlet.ResourceResponse;
|
||||
|
||||
import org.gcube.portlet.user.my_vres.constants.MyVresPortletKeys;
|
||||
import org.gcube.portlet.user.my_vres.shared.UserBelonging;
|
||||
import org.gcube.portlet.user.my_vres.shared.VRE;
|
||||
import org.gcube.vomanagement.usermanagement.GroupManager;
|
||||
import org.gcube.vomanagement.usermanagement.impl.LiferayGroupManager;
|
||||
import org.gcube.vomanagement.usermanagement.impl.LiferayUserManager;
|
||||
import org.gcube.vomanagement.usermanagement.model.GCubeGroup;
|
||||
import org.gcube.vomanagement.usermanagement.model.GCubeUser;
|
||||
import org.gcube.vomanagement.usermanagement.model.VirtualGroup;
|
||||
import org.gcube.vomanagement.usermanagement.util.ManagementUtils;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* MVCResourceCommand that returns the list of VREs in JSON, replacing legacy GWT RPC.
|
||||
*/
|
||||
@Component(
|
||||
property = {
|
||||
"javax.portlet.name=" + MyVresPortletKeys.MYVRES,
|
||||
"mvc.command.name=/myvres/get_user_vres"
|
||||
},
|
||||
service = MVCResourceCommand.class
|
||||
)
|
||||
public class GetUserVREsMVCResourceCommand implements MVCResourceCommand {
|
||||
|
||||
@Override
|
||||
public boolean serveResource(ResourceRequest resourceRequest, ResourceResponse resourceResponse) throws PortletException {
|
||||
// CSRF Validation: convert to HttpServletRequest and validate p_auth
|
||||
HttpServletRequest httpServletRequest = PortalUtil.getHttpServletRequest(resourceRequest);
|
||||
try {
|
||||
AuthTokenUtil.checkCSRFToken(httpServletRequest, GetUserVREsMVCResourceCommand.class.getName());
|
||||
} catch (PrincipalException pe) {
|
||||
// Wrap CSRF validation failure in PortletException
|
||||
throw new PortletException(pe);
|
||||
}
|
||||
|
||||
ThemeDisplay themeDisplay = (ThemeDisplay) resourceRequest.getAttribute(WebKeys.THEME_DISPLAY);
|
||||
// Example of reading parameters (if needed)
|
||||
String filter = ParamUtil.getString(resourceRequest, "filter");
|
||||
|
||||
LinkedHashMap<String, ArrayList<VRE>> data = getUserVREs(httpServletRequest);
|
||||
JSONObject root = JSONFactoryUtil.createJSONObject();
|
||||
JSONArray categories = JSONFactoryUtil.createJSONArray();
|
||||
|
||||
for (Map.Entry<String, ArrayList<VRE>> entry : data.entrySet()) {
|
||||
JSONObject category = JSONFactoryUtil.createJSONObject();
|
||||
// Risolve nome localizzato dei Virtual Group (evita XML)
|
||||
category.put("name", resolveLocalized(entry.getKey(), themeDisplay));
|
||||
JSONArray vres = JSONFactoryUtil.createJSONArray();
|
||||
for (VRE v : entry.getValue()) {
|
||||
JSONObject jv = JSONFactoryUtil.createJSONObject();
|
||||
// Risolve nome localizzato della VRE (evita XML)
|
||||
jv.put("name", resolveLocalized(v.getName(), themeDisplay));
|
||||
jv.put("description", v.getDescription());
|
||||
jv.put("imageURL", v.getImageURL());
|
||||
jv.put("context", v.getContext());
|
||||
jv.put("friendlyURL", v.getFriendlyURL());
|
||||
jv.put("userBelonging", v.getUserBelonging() != null ? v.getUserBelonging().name() : "");
|
||||
vres.put(jv);
|
||||
}
|
||||
category.put("vres", vres);
|
||||
categories.put(category);
|
||||
}
|
||||
root.put("categories", categories);
|
||||
|
||||
resourceResponse.setContentType("application/json");
|
||||
try (PrintWriter writer = resourceResponse.getWriter()) {
|
||||
writer.write(root.toString());
|
||||
} catch (IOException e) {
|
||||
throw new PortletException(e);
|
||||
}
|
||||
return false; // let portal continue default processing
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve real user VREs grouped by current site's Virtual Groups, aligned with 6.2 logic.
|
||||
*/
|
||||
protected static LinkedHashMap<String, ArrayList<VRE>> getUserVREs(HttpServletRequest req) {
|
||||
LinkedHashMap<String, ArrayList<VRE>> categories = new LinkedHashMap<>();
|
||||
try {
|
||||
GroupManager gm = new LiferayGroupManager();
|
||||
// Usa ThemeDisplay per ottenere il siteGroupId corrente (più affidabile di serverName)
|
||||
ThemeDisplay td = (ThemeDisplay) req.getAttribute(WebKeys.THEME_DISPLAY);
|
||||
long siteGroupId = td != null ? td.getSiteGroupId() : ManagementUtils.getSiteGroupIdFromServletRequest(req.getServerName());
|
||||
List<VirtualGroup> currentSiteVGroups = gm.getVirtualGroups(siteGroupId);
|
||||
for (VirtualGroup vg : currentSiteVGroups) {
|
||||
categories.put(vg.getName(), new ArrayList<>());
|
||||
}
|
||||
|
||||
long userId = td != null ? td.getUserId() : PortalUtil.getUserId(req);
|
||||
GCubeUser currUser = new LiferayUserManager().getUserById(userId);
|
||||
// VRE dell'utente nel sito corrente
|
||||
java.util.Set<GCubeGroup> userSiteGroups = gm.listGroupsByUserAndSiteGroupId(currUser.getUserId(), siteGroupId);
|
||||
// Fallback: se vuoto, usa tutte le VRE dell'utente
|
||||
java.util.Collection<GCubeGroup> sourceVREs = (userSiteGroups != null && !userSiteGroups.isEmpty())
|
||||
? userSiteGroups
|
||||
: gm.listVresByUser(currUser.getUserId());
|
||||
|
||||
for (GCubeGroup vre : sourceVREs) {
|
||||
VRE jvre = new VRE();
|
||||
jvre.setName(vre.getGroupName());
|
||||
jvre.setContext(gm.getInfrastructureScope(vre.getGroupId()));
|
||||
String logoURL = gm.getGroupLogoURL(vre.getLogoId());
|
||||
jvre.setImageURL(logoURL);
|
||||
// Replica 6.2: friendlyURL del sito della VRE, senza /workspace
|
||||
String friendly = "/group" + vre.getFriendlyURL();
|
||||
jvre.setFriendlyURL(friendly);
|
||||
jvre.setUserBelonging(UserBelonging.BELONGING);
|
||||
|
||||
List<VirtualGroup> vreGroups = gm.getVirtualGroups(vre.getGroupId());
|
||||
for (VirtualGroup vreGroup : vreGroups) {
|
||||
ArrayList<VRE> list = categories.get(vreGroup.getName());
|
||||
if (list != null) list.add(jvre);
|
||||
}
|
||||
}
|
||||
return categories;
|
||||
} catch (Exception e) {
|
||||
// On error, return empty categories map to avoid UI breakage
|
||||
return categories;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean containsGroup(java.util.Collection<GCubeGroup> groups, GCubeGroup target) {
|
||||
if (groups == null || target == null) return false;
|
||||
for (GCubeGroup g : groups) {
|
||||
if (g.getGroupId() == target.getGroupId()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String moduleResource(String relative) {
|
||||
String cleaned = relative == null ? "" : relative.replaceFirst("^/", "");
|
||||
// Web context path for modules under /o/<Bundle-SymbolicName>/...
|
||||
return "/o/" + org.gcube.portlet.user.my_vres.constants.MyVresPortletKeys.BUNDLE_SYMBOLIC_NAME + "/" + cleaned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Risolve stringhe localizzate Liferay (XML) restituendo il valore nella lingua corrente
|
||||
* o nel default locale; se non è XML, restituisce il valore così com'è.
|
||||
*/
|
||||
private static String resolveLocalized(String value, ThemeDisplay td) {
|
||||
if (value == null) return "";
|
||||
String trimmed = value.trim();
|
||||
// Se non è un campo localizzato (XML), usa direttamente la stringa
|
||||
if (!trimmed.startsWith("<?xml")) return trimmed;
|
||||
|
||||
String langId = td != null ? LocaleUtil.toLanguageId(td.getLocale()) : LocaleUtil.toLanguageId(LocaleUtil.getDefault());
|
||||
String localized = LocalizationUtil.getLocalization(trimmed, langId);
|
||||
if (Validator.isNotNull(localized)) return localized;
|
||||
|
||||
// Fallback al default-locale definito nel campo localizzato
|
||||
String defaultLangId = LocalizationUtil.getDefaultLanguageId(trimmed);
|
||||
localized = LocalizationUtil.getLocalization(trimmed, defaultLangId);
|
||||
return Validator.isNotNull(localized) ? localized : trimmed;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package org.gcube.portlet.user.my_vres.shared;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class AuthorizationBean implements Serializable {
|
||||
private String oAuth2TemporaryCode;
|
||||
private String state;
|
||||
private boolean success;
|
||||
private String errorDescription;
|
||||
|
||||
public AuthorizationBean() { super(); }
|
||||
|
||||
public AuthorizationBean(String oAuth2TemporaryCode, String state, boolean success, String errorDescription) {
|
||||
super();
|
||||
this.oAuth2TemporaryCode = oAuth2TemporaryCode;
|
||||
this.state = state;
|
||||
this.success = success;
|
||||
this.errorDescription = errorDescription;
|
||||
}
|
||||
|
||||
public String getOAuth2TemporaryCode() { return oAuth2TemporaryCode; }
|
||||
public void setOAuth2TemporaryCode(String oAuth2TemporaryCode) { this.oAuth2TemporaryCode = oAuth2TemporaryCode; }
|
||||
|
||||
public String getState() { return state; }
|
||||
public void setState(String state) { this.state = state; }
|
||||
|
||||
public boolean isSuccess() { return success; }
|
||||
public void setSuccess(boolean success) { this.success = success; }
|
||||
|
||||
public String getErrorDescription() { return errorDescription; }
|
||||
public void setErrorDescription(String errorDescription) { this.errorDescription = errorDescription; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AuthorizationBean [oAuth2TemporaryCode=" + oAuth2TemporaryCode + ", state=" + state + ", success=" + success + ", errorDescription="
|
||||
+ errorDescription + "]";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package org.gcube.portlet.user.my_vres.shared;
|
||||
|
||||
import java.io.Serializable;
|
||||
/**
|
||||
* Minimal shared DTO to support GWT RPC payloads.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ResearchEnvironment implements Serializable{
|
||||
private String name;
|
||||
private String description;
|
||||
private String imageURL;
|
||||
// the infrastructure scope
|
||||
private String context;
|
||||
private String friendlyURL;
|
||||
private UserBelonging userBelonging;
|
||||
|
||||
public ResearchEnvironment() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ResearchEnvironment(String name, String description,
|
||||
String imageURL, String context, String friendlyURL,
|
||||
UserBelonging userBelonging) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.imageURL = imageURL;
|
||||
this.context = context;
|
||||
this.friendlyURL = friendlyURL;
|
||||
this.userBelonging = userBelonging;
|
||||
}
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
|
||||
public String getImageURL() { return imageURL; }
|
||||
public void setImageURL(String imageURL) { this.imageURL = imageURL; }
|
||||
|
||||
/**
|
||||
* @return the infrastructure scope
|
||||
*/
|
||||
public String getContext() { return context; }
|
||||
public void setContext(String context) { this.context = context; }
|
||||
|
||||
public String getFriendlyURL() { return friendlyURL; }
|
||||
public void setFriendlyURL(String friendlyURL) { this.friendlyURL = friendlyURL; }
|
||||
|
||||
public UserBelonging getUserBelonging() { return userBelonging; }
|
||||
public void setUserBelonging(UserBelonging userBelonging) { this.userBelonging = userBelonging; }
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package org.gcube.portlet.user.my_vres.shared;
|
||||
|
||||
public enum UserBelonging { BELONGING, NOT_BELONGING, PENDING }
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package org.gcube.portlet.user.my_vres.shared;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class VRE extends ResearchEnvironment implements Serializable, Comparable<VRE> {
|
||||
public VRE() { super(); }
|
||||
|
||||
public VRE(String vreName, String description, String imageURL,
|
||||
String vomsGroupName, String friendlyURL,
|
||||
UserBelonging userBelonging) {
|
||||
super(vreName, description, imageURL, vomsGroupName, friendlyURL, userBelonging);
|
||||
}
|
||||
|
||||
public int compareTo(VRE arg0) {
|
||||
return this.getName().compareTo(arg0.getName());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/* Legacy CSS adapted: ensure correct image paths relative to module resources */
|
||||
|
||||
div.vreButton { float: left; }
|
||||
|
||||
a.vrelink, a.vrelink:active, a.vrelink:visited { cursor: pointer; text-decoration: none; color: #15C; }
|
||||
a.vrelink:hover { opacity: 0.8; text-decoration: underline; }
|
||||
|
||||
.gwt-Button { margin: 0; padding: 0; }
|
||||
|
||||
.mainPanel { margin-bottom: 15px; }
|
||||
.flowPanel { margin-left: 5px; }
|
||||
.important { font-weight: bold; }
|
||||
.NoVresStyle { line-height: 25px; padding-left: 0px; }
|
||||
|
||||
.listPanel { margin-right: 10px; margin-top: 5px; padding: 5px; }
|
||||
.category-panel { width: 95%; }
|
||||
.category-name { margin-top: 8px; color: #999; font-family: 'lucida grande', tahoma, verdana, arial, sans-serif; font-size: 12px; font-variant: small-caps; font-weight: bold; }
|
||||
|
||||
.switcher:hover { opacity: 0.8; cursor: pointer; }
|
||||
|
||||
.listPanel-title { color: #999; font-family: 'lucida grande', tahoma, verdana, arial, sans-serif; font-size: 12px; font-variant: small-caps; font-weight: bold; }
|
||||
|
||||
/* Grid layout adjustments to mimic legacy card grid */
|
||||
.list { list-style-type: none; margin: 10px 0; padding: 0; display: flex; flex-wrap: wrap; gap: 12px; }
|
||||
|
||||
.list-item { list-style: none; margin: 0; padding: 0; }
|
||||
|
||||
/* Card visual specs */
|
||||
.vreButton { background: transparent; margin-right: 10px; margin-top: 5px; padding: 5px; border-radius: 6px; background-color: #FFF; border: 1px solid #DBDBDB; cursor: pointer; width: 120px; height: 140px; position: relative; background-position: 50% 60%; background-repeat: no-repeat; background-size: 72px 72px; }
|
||||
|
||||
.vreCaption { font-family: Arial, Helvetica, sans-serif; color: #39C; display: block; font-size: 12px; font-weight: bold; text-align: left; padding-left: 6px; border-bottom: 4px solid #39C; position: absolute; top: 0; left: 0; right: 0; height: 20px; line-height: 20px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; background: #fff; }
|
||||
|
||||
.vreButton[disabled]:hover, .vreButton[disabled]:active, .vreButton[disabled] { color: #CCC; border-color: #BBB; cursor: default; opacity: 1.0; }
|
||||
.vreButton:hover { border-color: #39C; color: #222; opacity: 0.9; }
|
||||
.vreButton:active { background: #fff; border: 1px solid #DBDBDB; }
|
||||
|
||||
/* Default icons for card types */
|
||||
.vlab-default { background: url('../images/vlab.png') 50% 50% no-repeat; }
|
||||
.item-vre { background-image: url('../images/vres.png'); }
|
||||
.item-vo { background-image: url('../images/vos.png'); }
|
||||
|
||||
/* Add More tile visual specs */
|
||||
.more-vre { width: 120px; height: 140px; background-image: url('../images/More.png'); background-repeat: no-repeat; background-position: 50% 60%; background-size: 36px 36px; }
|
||||
|
||||
.list-item:hover { cursor: pointer; color: #336699; text-decoration: none; transition: color .25s ease-in-out; -moz-transition: color .25s ease-in-out; -webkit-transition: color .25s ease-in-out; }
|
||||
|
||||
.vreButton { background: transparent; margin-right: 10px; margin-top: 5px; padding: 5px; border-radius: 6px; background-color: #FFF; border: 1px solid #DBDBDB; cursor: pointer; }
|
||||
.vreCaption { font-family: Arial, Helvetica, sans-serif; color: #39C; display: block; font-size: 9px; font-weight: bold; margin-bottom: 3px; text-align: left; padding-left: 1px; border-bottom: 4px solid #39C; }
|
||||
.vreButton[disabled]:hover, .vreButton[disabled]:active, .vreButton[disabled] { color: #CCC; border-color: #BBB; cursor: default; opacity: 1.0; }
|
||||
.vreButton:hover { border-color: #39C; color: #222; opacity: 0.8; }
|
||||
.vreButton:active { background: #fff; border: 1px solid #DBDBDB; }
|
||||
|
||||
/* Legacy list layout (no flex) as in 6.2 */
|
||||
.list { list-style-type: none; margin-bottom: 10px; margin-top: 5px; margin-left: 0; padding: 0 0 0 5px; }
|
||||
.list-item { color: #444; font-family: 'Lucida Grande', Verdana, 'Bitstream Vera Sans', Arial, sans-serif; font-size: 15px; margin-top: 5px; line-height: 14px; padding-left: 0; background-color: transparent; background-repeat: no-repeat; }
|
||||
|
||||
/* Card visual specs (120x140) */
|
||||
.vreButton { background: transparent; margin-right: 10px; margin-top: 5px; padding: 5px; border-radius: 6px; background-color: #FFF; border: 1px solid #DBDBDB; cursor: pointer; width: 120px; height: 140px; position: relative; background-position: 50% 60%; background-repeat: no-repeat; background-size: contain; box-shadow: none; }
|
||||
.vreButton:hover { border-color: #39C; color: #222; opacity: 0.9; }
|
||||
.vreButton[disabled]:hover, .vreButton[disabled]:active, .vreButton[disabled] { color: #CCC; border-color: #BBB; cursor: default; opacity: 1.0; }
|
||||
|
||||
/* Caption bar (legacy blue underline) */
|
||||
.vreCaption { font-family: Arial, Helvetica, sans-serif; color: #39C; display: block; font-size: 12px; font-weight: bold; text-align: left; padding-left: 6px; border-bottom: 4px solid #39C; position: absolute; top: 0; left: 0; right: 0; height: 20px; line-height: 20px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
|
||||
/* Default icons for card types */
|
||||
.vlab-default { background: url('../images/vlab.png') 50% 50% no-repeat; }
|
||||
.item-vre { background-image: url('../images/vres.png'); }
|
||||
.item-vo { background-image: url('../images/vos.png'); }
|
||||
|
||||
/* Add More tile */
|
||||
.more-vre { width: 120px; height: 140px; background-image: url('../images/More.png'); background-repeat: no-repeat; background-position: 50% 60%; background-size: 36px 36px; }
|
||||
|
||||
/* Container panels */
|
||||
.listPanel { margin-right: 10px; margin-top: 5px; padding: 5px; }
|
||||
.category-panel { width: 95%; }
|
||||
.category-name { margin-top: 8px; color: #999; font-family: 'lucida grande', tahoma, verdana, arial, sans-serif; font-size: 12px; font-variant: small-caps; font-weight: bold; }
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/* MyVREs styles aligned to legacy 6.2 MyVREs.css */
|
||||
.my-vres-wrapper {
|
||||
/* Category heading: small-caps, grey, bold */
|
||||
.category-name {
|
||||
margin-top: 8px;
|
||||
color: #999;
|
||||
font-family: 'lucida grande', tahoma, verdana, arial, sans-serif;
|
||||
font-size: 12px;
|
||||
font-variant: small-caps;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.category-panel {
|
||||
width: 95%;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
/* Grid behaves like float-left legacy tiles */
|
||||
.vre-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* Card tile visuals (legacy .vreButton) */
|
||||
.card.vre-card {
|
||||
background: #FFF;
|
||||
margin-top: 5px;
|
||||
padding: 5px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #DBDBDB;
|
||||
cursor: pointer;
|
||||
box-shadow: none;
|
||||
width: max-content; /* card si allarga quanto serve per il titolo */
|
||||
min-width: 120px; /* non più stretta del legacy */
|
||||
height: 140px; /* assicura che caption+immagine stiano dentro */
|
||||
overflow: hidden; /* impedisce che titolo/divider escano dalla card */
|
||||
|
||||
.vre-card-header {
|
||||
display: flex;
|
||||
flex-direction: column; /* title bar above image */
|
||||
align-items: stretch;
|
||||
gap: 6px;
|
||||
padding: 0; /* legacy had tight padding */
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* Caption bar (legacy .vreCaption) */
|
||||
.vre-card-title {
|
||||
.vre-name {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: #39C;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
padding-left: 6px;
|
||||
padding-bottom: 10px; /* separazione di ~10px dal divider blu */
|
||||
border-bottom: 4px solid #39C;
|
||||
line-height: 20px;
|
||||
height: auto; /* consente spazio extra del padding */
|
||||
white-space: nowrap; /* resta su una riga e allarga la card */
|
||||
}
|
||||
}
|
||||
|
||||
/* Image box similar to legacy 100px area */
|
||||
.vre-image-box {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
border: none;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.vre-logo {
|
||||
width: 84px;
|
||||
height: 84px;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: #39C;
|
||||
color: #222;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* Disabled styles (parity with legacy) */
|
||||
&[disabled], &[aria-disabled='true'] {
|
||||
color: #CCC;
|
||||
border-color: #BBB;
|
||||
cursor: default;
|
||||
opacity: 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add More: l'intera card è l'immagine di sfondo */
|
||||
.vre-card.add-more {
|
||||
background-image: url('../images/More.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
background-size: cover; /* tutta la card è immagine */
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 668 B |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 464 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1,14 @@
|
|||
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
|
||||
|
||||
<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
|
||||
<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %>
|
||||
<%@ taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui" %>
|
||||
<%@ page import="org.gcube.portlet.user.my_vres.constants.MyVresPortletKeys" %>
|
||||
|
||||
<%@ taglib uri="http://liferay.com/tld/portlet" prefix="liferay-portlet" %><%@
|
||||
taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %><%@
|
||||
taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui" %>
|
||||
|
||||
<liferay-theme:defineObjects />
|
||||
|
||||
<portlet:defineObjects />
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Configurazione MyVREs – personalizza il comportamento delle card
|
||||
window.MyVREsConfig = {
|
||||
// Se presente friendlyURL e l’utente appartiene alla VRE, vai direttamente al workspace
|
||||
preferFriendlyURL: true,
|
||||
// Stato della finestra del JoinVRE (es. 'maximized', 'normal')
|
||||
joinWindowState: 'maximized',
|
||||
// Apri la pagina di JoinVRE in una nuova tab (false = stessa finestra)
|
||||
openJoinInNewTab: false,
|
||||
// Azione quando lo stato è PENDING: 'join' (consigliato) oppure 'workspace' se disponibile
|
||||
behaviorOnPending: 'join',
|
||||
// Azione quando NON appartiene: 'join' (consigliato) oppure 'workspace' se disponibile
|
||||
behaviorOnNotBelonging: 'join'
|
||||
};
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>MyVREs Legacy Style Preview</title>
|
||||
<link rel="stylesheet" href="css/main.css" />
|
||||
<style>
|
||||
body { font-family: Arial, Helvetica, sans-serif; padding: 20px; }
|
||||
.my-vres-wrapper { max-width: 700px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="myVREsDIV" class="my-vres-wrapper">
|
||||
<div class="category-panel">
|
||||
<div class="category-name">Virtual Group A</div>
|
||||
<div class="vre-grid">
|
||||
<div class="card vre-card">
|
||||
<div class="vre-card-header">
|
||||
<div class="vre-card-title"><h4 class="vre-name">CCP</h4></div>
|
||||
<div class="vre-image-box"><img class="vre-logo" src="images/vres.png" alt="logo"/></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card vre-card">
|
||||
<div class="vre-card-header">
|
||||
<div class="vre-card-title"><h4 class="vre-name">devVRE</h4></div>
|
||||
<div class="vre-image-box"><img class="vre-logo" src="images/vres.png" alt="logo"/></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="category-panel">
|
||||
<div class="category-name">Virtual Group C</div>
|
||||
<div class="vre-grid">
|
||||
<div class="card vre-card">
|
||||
<div class="vre-card-header">
|
||||
<div class="vre-card-title"><h4 class="vre-name">devVRE</h4></div>
|
||||
<div class="vre-image-box"><img class="vre-logo" src="images/vres.png" alt="logo"/></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="category-panel">
|
||||
<div class="category-name">Add More</div>
|
||||
<div class="vre-grid">
|
||||
<div class="card vre-card add-more"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
<%@ include file="/init.jsp" %>
|
||||
|
||||
<portlet:defineObjects />
|
||||
|
||||
<portlet:resourceURL id="/myvres/get_user_vres" var="getUserVREsURL" />
|
||||
<liferay-portlet:renderURL portletName="org_gcube_portlets_user_joinvre_JoinVREPortlet" var="openJoinVREURL" />
|
||||
|
||||
<!-- Use SCSS-compiled main.css aligned to join-vre-portlet style -->
|
||||
<link rel="stylesheet" href="<%=request.getContextPath()%>/css/main.css" />
|
||||
<script src="<%=request.getContextPath()%>/js/config.js"></script>
|
||||
|
||||
<!-- Main container for MyVREs -->
|
||||
<div id="myVREsDIV" class="my-vres-wrapper"></div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var resourceURL = '<%= getUserVREsURL %>';
|
||||
var url = new URL(resourceURL, window.location.origin);
|
||||
url.searchParams.set('p_auth', Liferay.authToken);
|
||||
|
||||
function normalizeImageURL(imgSrc) {
|
||||
imgSrc = imgSrc || '';
|
||||
if (!imgSrc) return '';
|
||||
// Absolute URL
|
||||
if (/^https?:\/\//.test(imgSrc)) {
|
||||
return imgSrc;
|
||||
}
|
||||
// Already an OSGi resource path or other portal absolute path
|
||||
if (imgSrc.indexOf('/o/') === 0 || /^\/(group|documents|image)\//.test(imgSrc)) {
|
||||
return Liferay.ThemeDisplay.getPathContext() + imgSrc;
|
||||
}
|
||||
// Fallback: treat as module-relative resource (e.g., images/sampleImage.jpeg)
|
||||
return Liferay.ThemeDisplay.getPathContext() + '/o/org.gcube.portlet.user.my_vres/' + imgSrc.replace(/^\/?/, '');
|
||||
}
|
||||
|
||||
function buildJoinVREURL(params) {
|
||||
var jurl = new URL('<%= openJoinVREURL %>', window.location.origin);
|
||||
// windowState corretto e redirect alla pagina corrente
|
||||
var cfg = window.MyVREsConfig || {};
|
||||
jurl.searchParams.set('p_p_state', cfg.joinWindowState || 'maximized');
|
||||
jurl.searchParams.set('redirect', window.location.href);
|
||||
// pass through context/friendlyURL if available
|
||||
if (params) {
|
||||
Object.keys(params).forEach(function(k){
|
||||
if (params[k] != null) jurl.searchParams.set(k, params[k]);
|
||||
});
|
||||
}
|
||||
return jurl.toString();
|
||||
}
|
||||
|
||||
function buildExploreURL() {
|
||||
// Comportamento 6.2: Add More apre la pagina di esplorazione del sito corrente (area privata)
|
||||
// Costruiamo l'URL con friendlyURL del gruppo corrente
|
||||
return Liferay.ThemeDisplay.getPathContext() + '/group' + '<%= themeDisplay.getScopeGroup().getFriendlyURL() %>' + '/explore';
|
||||
}
|
||||
|
||||
function openVRE(v) {
|
||||
var cfg = window.MyVREsConfig || {};
|
||||
var belonging = (v && v.userBelonging) || '';
|
||||
var hasFriendly = !!(v && v.friendlyURL);
|
||||
|
||||
// Se appartiene e c’è friendlyURL, apri direttamente il workspace
|
||||
if (belonging === 'BELONGING' && hasFriendly && cfg.preferFriendlyURL !== false) {
|
||||
var target = v.friendlyURL;
|
||||
if (!/^https?:\/\//.test(target)) {
|
||||
target = Liferay.ThemeDisplay.getPathContext() + target;
|
||||
}
|
||||
window.location.href = target;
|
||||
return;
|
||||
}
|
||||
|
||||
// Stati NON appartenente o in attesa: comportamento da config (default: join)
|
||||
var action = 'join';
|
||||
if (belonging === 'PENDING') action = cfg.behaviorOnPending || 'join';
|
||||
else if (belonging === 'NOT_BELONGING') action = cfg.behaviorOnNotBelonging || 'join';
|
||||
|
||||
if (action === 'workspace' && hasFriendly) {
|
||||
var target2 = v.friendlyURL;
|
||||
if (!/^https?:\/\//.test(target2)) {
|
||||
target2 = Liferay.ThemeDisplay.getPathContext() + target2;
|
||||
}
|
||||
window.location.href = target2;
|
||||
return;
|
||||
}
|
||||
|
||||
// Default: vai al JoinVRE portlet con il context della VRE
|
||||
var ctx = v && v.context ? v.context : '';
|
||||
var jurl = buildJoinVREURL({ context: ctx });
|
||||
if (cfg.openJoinInNewTab) window.open(jurl, '_blank');
|
||||
else window.location.href = jurl;
|
||||
}
|
||||
|
||||
Liferay.Util.fetch(url.toString(), { method: 'GET' })
|
||||
.then(function(response) { return response.json(); })
|
||||
.then(function(response) {
|
||||
var container = document.getElementById('myVREsDIV');
|
||||
container.innerHTML = '';
|
||||
if (response && response.categories) {
|
||||
response.categories.forEach(function(cat) {
|
||||
// Category panel
|
||||
var panel = document.createElement('div');
|
||||
panel.className = 'category-panel';
|
||||
|
||||
var catName = document.createElement('div');
|
||||
catName.className = 'category-name';
|
||||
catName.textContent = cat.name;
|
||||
panel.appendChild(catName);
|
||||
|
||||
// Grid container similar to legacy float tiles
|
||||
var grid = document.createElement('div');
|
||||
grid.className = 'vre-grid';
|
||||
grid.className = 'vre-grid';
|
||||
|
||||
(cat.vres || []).forEach(function(v) {
|
||||
var card = document.createElement('div');
|
||||
card.className = 'card vre-card';
|
||||
card.setAttribute('role', 'button');
|
||||
card.setAttribute('tabindex', '0');
|
||||
|
||||
var header = document.createElement('div');
|
||||
header.className = 'vre-card-header';
|
||||
|
||||
var title = document.createElement('div');
|
||||
title.className = 'vre-card-title';
|
||||
var h4 = document.createElement('h4');
|
||||
h4.className = 'vre-name';
|
||||
h4.textContent = v.name || '';
|
||||
title.appendChild(h4);
|
||||
|
||||
var imgBox = document.createElement('div');
|
||||
imgBox.className = 'vre-image-box';
|
||||
var logo = document.createElement('img');
|
||||
logo.className = 'vre-logo';
|
||||
var imgSrc = normalizeImageURL(v.imageURL);
|
||||
logo.src = imgSrc || (Liferay.ThemeDisplay.getPathContext() + '/o/org.gcube.portlet.user.my_vres/images/vres.png');
|
||||
logo.alt = (v.name || '') + ' logo';
|
||||
logo.onerror = function(){ this.onerror=null; this.src = Liferay.ThemeDisplay.getPathContext() + '/o/org.gcube.portlet.user.my_vres/images/vres.png'; };
|
||||
imgBox.appendChild(logo);
|
||||
|
||||
// Append title first, then image box (title bar at top)
|
||||
header.appendChild(title);
|
||||
header.appendChild(imgBox);
|
||||
card.appendChild(header);
|
||||
|
||||
// Click / keyboard behavior
|
||||
card.addEventListener('click', function(){ openVRE(v); });
|
||||
card.addEventListener('keydown', function(e){ if (e.key === 'Enter' || e.keyCode === 13) { openVRE(v); } });
|
||||
|
||||
grid.appendChild(card);
|
||||
});
|
||||
|
||||
panel.appendChild(grid);
|
||||
container.appendChild(panel);
|
||||
});
|
||||
|
||||
// Add More section (legacy: heading + tile without caption)
|
||||
var morePanel = document.createElement('div');
|
||||
morePanel.className = 'category-panel';
|
||||
var moreHeading = document.createElement('div');
|
||||
moreHeading.className = 'category-name';
|
||||
moreHeading.textContent = 'Add More';
|
||||
morePanel.appendChild(moreHeading);
|
||||
|
||||
var moreGrid = document.createElement('div');
|
||||
moreGrid.className = 'vre-grid';
|
||||
|
||||
var more = document.createElement('div');
|
||||
more.className = 'card vre-card add-more';
|
||||
more.title = 'Add more VREs';
|
||||
more.setAttribute('role', 'button');
|
||||
more.setAttribute('tabindex', '0');
|
||||
|
||||
|
||||
more.addEventListener('click', function(){
|
||||
window.location.href = buildExploreURL();
|
||||
});
|
||||
more.addEventListener('keydown', function(e){ if (e.key === 'Enter' || e.keyCode === 13) { window.location.href = buildExploreURL(); } });
|
||||
|
||||
moreGrid.appendChild(more);
|
||||
morePanel.appendChild(moreGrid);
|
||||
container.appendChild(morePanel);
|
||||
} else {
|
||||
container.innerHTML = '<div class="alert alert-info">Nessun dato disponibile</div>';
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error('Richiesta JSON fallita:', err);
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
javax.portlet.title.org_gcube_portlet_user_my_vres_MyVresPortlet=MyVres
|
||||
# Removed dev caption to avoid extra text in UI
|
||||
myvres.caption=
|
||||