diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a77d911b5..0f3f876d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ on: push: branches: - - master + - main - develop pull_request: @@ -30,7 +30,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: github branch run: | if [ "${{ github.event.release.target_commitish }}" != "" ]; then @@ -39,7 +39,7 @@ jobs: BRANCH=${GITHUB_REF##*/} fi echo "GITHUB_BRANCH=${BRANCH}" >> $GITHUB_ENV - if [ "$BRANCH" == "master" ]; then + if [ "$BRANCH" == "main" ]; then echo "CLOWDER_VERSION=$(awk '/version = / { print $4 }' project/Build.scala | sed 's/"//g')" >> $GITHUB_ENV elif [ "$BRANCH" == "develop" ]; then echo "CLOWDER_VERSION=develop" >> $GITHUB_ENV @@ -51,12 +51,12 @@ jobs: distribution: 'zulu' java-version: 8 - name: Cache SBT ivy cache - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ~/.ivy2/cache key: ${{ runner.os }}-sbt-ivy-cache-${{ hashFiles('project/Build.scala') }} - name: Cache SBT - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ~/.sbt key: ${{ runner.os }}-sbt-${{ hashFiles('project/Build.scala') }} @@ -85,7 +85,7 @@ jobs: ports: - 27017:27017 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: github branch run: | if [ "${{ github.event.release.target_commitish }}" != "" ]; then @@ -94,7 +94,7 @@ jobs: BRANCH=${GITHUB_REF##*/} fi echo "GITHUB_BRANCH=${BRANCH}" >> $GITHUB_ENV - if [ "$BRANCH" == "master" ]; then + if [ "$BRANCH" == "main" ]; then echo "CLOWDER_VERSION=$(awk '/version = / { print $4 }' project/Build.scala | sed 's/"//g')" >> $GITHUB_ENV elif [ "$BRANCH" == "develop" ]; then echo "CLOWDER_VERSION=develop" >> $GITHUB_ENV @@ -130,7 +130,7 @@ jobs: runs-on: ubuntu-latest needs: build steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: github branch run: | if [ "${{ github.event.release.target_commitish }}" != "" ]; then @@ -139,7 +139,7 @@ jobs: BRANCH=${GITHUB_REF##*/} fi echo "GITHUB_BRANCH=${BRANCH}" >> $GITHUB_ENV - if [ "$BRANCH" == "master" ]; then + if [ "$BRANCH" == "main" ]; then echo "CLOWDER_VERSION=$(awk '/version = / { print $4 }' project/Build.scala | sed 's/"//g')" >> $GITHUB_ENV elif [ "$BRANCH" == "develop" ]; then echo "CLOWDER_VERSION=develop" >> $GITHUB_ENV @@ -177,13 +177,13 @@ jobs: done rm ${ZIPFILE} zip -r ${ZIPFILE} ${DIR} - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 with: name: clowder.zip path: target/universal/clowder-*.zip - name: Upload files to a GitHub release if: github.event_name == 'release' && github.event.action == 'created' - uses: svenstaro/upload-release-action@1.1.0 + uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ github.ref }} @@ -207,7 +207,7 @@ jobs: runs-on: ubuntu-latest needs: build steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: github branch run: | if [ "${{ github.event.release.target_commitish }}" != "" ]; then @@ -216,7 +216,7 @@ jobs: BRANCH=${GITHUB_REF##*/} fi echo "GITHUB_BRANCH=${BRANCH}" >> $GITHUB_ENV - if [ "$BRANCH" == "master" ]; then + if [ "$BRANCH" == "main" ]; then echo "CLOWDER_VERSION=$(awk '/version = / { print $4 }' project/Build.scala | sed 's/"//g')" >> $GITHUB_ENV elif [ "$BRANCH" == "develop" ]; then echo "CLOWDER_VERSION=develop" >> $GITHUB_ENV @@ -237,10 +237,10 @@ jobs: with: path: ~/.sbt key: ${{ runner.os }}-sbt-${{ hashFiles('project/Build.scala') }} - - name: Set up Python 3.7 - uses: actions/setup-python@v1 + - name: Set up Python 3.11 + uses: actions/setup-python@v5 with: - python-version: 3.7 + python-version: 3.11 - name: sbt doc run: ./sbt doc env: @@ -248,7 +248,7 @@ jobs: VERSION: ${{ env.CLOWDER_VERSION }} BUILDNUMBER: ${{ github.run_number }} GITSHA1: ${{ github.sha }} - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 with: name: ScalaDoc path: target/scala-*/api/ @@ -266,7 +266,7 @@ jobs: cd doc/src/sphinx/ python -m pip install -r requirements.txt make html epub - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 with: name: HTML Documentation path: doc/src/sphinx/_build/html @@ -279,13 +279,13 @@ jobs: key: ${{ secrets.SCP_KEY }} files: "doc/src/sphinx/_build/html/*" target: "CATS/${{ env.CLOWDER_VERSION }}/documentation/sphinx" - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 with: name: EPUB Documentation path: doc/src/sphinx/_build/epub/Clowder.epub - name: Upload files to a GitHub release if: github.event_name == 'release' && github.event.action == 'created' - uses: svenstaro/upload-release-action@1.1.0 + uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ github.ref }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 7702ae180..5e1053543 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -5,7 +5,7 @@ name: Docker # and would not set the right version flags. # This will run when: -# - when new code is pushed to master/develop to push the tags +# - when new code is pushed to main/develop to push the tags # latest and develop # - when a pull request is created and updated to make sure the # Dockerfile is still valid. @@ -16,14 +16,14 @@ name: Docker on: push: branches: - - master + - main - develop pull_request: -# Certain actions will only run when this is the master repo. +# Certain actions will only run when this is the main repo. env: - MASTER_REPO: clowder-framework/clowder + MAIN_REPO: clowder-framework/clowder DOCKERHUB_ORG: clowder jobs: @@ -64,7 +64,7 @@ jobs: PLATFORM: "linux/amd64" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # calculate some variables that are used later - name: variable setup @@ -77,7 +77,7 @@ jobs: BRANCH=${GITHUB_REF##*/} fi - if [ "$BRANCH" == "master" ]; then + if [ "$BRANCH" == "main" ]; then version="$(awk '/version = / { print $4 }' project/Build.scala | sed 's/"//g')" tags="latest" oldversion="" @@ -115,11 +115,11 @@ jobs: # setup docker build - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Inspect Builder run: | @@ -132,13 +132,13 @@ jobs: # login to registries - name: Login to DockerHub if: env.dockerhub != '' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: Login to GitHub Container Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -147,7 +147,7 @@ jobs: # build the clowder docker images - name: Build and push ${{ matrix.IMAGE }}-build if: matrix.IMAGE == 'clowder' - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v6 with: push: true context: ${{ matrix.FOLDER }} @@ -164,7 +164,7 @@ jobs: - name: Build and push ${{ matrix.IMAGE }}-runtime if: matrix.IMAGE == 'clowder' - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v6 with: push: true context: ${{ matrix.FOLDER }} @@ -182,7 +182,7 @@ jobs: # build the other docker images - name: Build and push ${{ matrix.IMAGE }} if: matrix.IMAGE != 'clowder' - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v6 with: push: true context: ${{ matrix.FOLDER }} @@ -198,10 +198,10 @@ jobs: # update README at DockerHub - name: Docker Hub Description - if: env.dockerhub != '' && matrix.README != '' && github.event_name == 'push' && github.repository == env.MASTER_REPO && env.BRANCH == 'master' - uses: peter-evans/dockerhub-description@v2 - env: - DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} - DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} - DOCKERHUB_REPOSITORY: ${{ env.DOCKERHUB_ORG }}/${{ matrix.IMAGE }} - README_FILEPATH: ${{ matrix.README }} + if: env.dockerhub != '' && matrix.README != '' && github.event_name == 'push' && github.repository == env.MAIN_REPO && env.BRANCH == 'main' + uses: peter-evans/dockerhub-description@v4 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + repository: ${{ env.DOCKERHUB_ORG }}/${{ matrix.IMAGE }} + readme-filepath : ${{ matrix.README }} diff --git a/.github/workflows/swagger.yml b/.github/workflows/swagger.yml index b55dfb26f..ad68e05e5 100644 --- a/.github/workflows/swagger.yml +++ b/.github/workflows/swagger.yml @@ -1,14 +1,14 @@ name: swagger # This will run when: -# - when new code is pushed to master/develop to make sure the +# - when new code is pushed to main/develop to make sure the # code does compile. # - when a pull request is created and updated to make sure the # code does compile. on: push: branches: - - master + - main - develop pull_request: @@ -20,7 +20,7 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: openapi-lint uses: mbowman100/swagger-validator-action@master diff --git a/CHANGELOG.md b/CHANGELOG.md index 37eb95678..34db94913 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## 1.23.0 - 2025-07-31 + +### Fixed +- Fixed XSS in space creation by escaping name and description field. +- Fixed `NoSuchElementException` in spaces listing page when user is not defined. The error occurred when calling + `user.get.id` on an undefined user in the spaces ownership dropdown. Added proper user existence checks in + `listSpaces.scala.html` and `miniList.scala.html` templates. +- Removed refrences to repo.typesafe.com from sbt-launch.jar and build.scala +- Removed iRods integration and dependencies. The iRods file storage service and plugin have been completely removed + from the codebase. Users who were using iRods for file storage will need to configure an alternative storage + backend (filesystem, MongoDB GridFS, or AWS S3). + ## 1.22.1 - 2023-11-10 ### Fixed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3a2fd3f99..0187dc6d2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,7 @@ below. By participating in this project, you agree to abide by the [code of cond Before your code can be accepted, you will have to sign a [CLA](https://clowderframework.org/pdf/Clowder-CLA.pdf) and mail it to lmarini@illinois.edu. -Most of the core development happens on [NCSA Bitbucket][bitbucket]. The `master` and `develop` branches are pushed to +Most of the core development happens on [NCSA Bitbucket][bitbucket]. The `` and `develop` branches are pushed to [GitHub][github] nightly. We encourage contributors to create an account on [NCSA Bitbucket][bitbucket] and make pull requests there. We also accept diff --git a/Dockerfile b/Dockerfile index 3ef75036b..4e03e13dc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # ---------------------------------------------------------------------- # BUILD CLOWDER DIST # ---------------------------------------------------------------------- -FROM openjdk:8-jdk-bullseye as clowder-build +FROM openjdk:8-jdk-bullseye AS clowder-build ARG BRANCH="unknown" ARG VERSION="unknown" @@ -40,7 +40,7 @@ RUN rm -rf target/universal/clowder-*.zip clowder clowder-* \ # ---------------------------------------------------------------------- # BUILD CLOWDER # ---------------------------------------------------------------------- -FROM openjdk:8-jre-bullseye as clowder-runtime +FROM openjdk:8-jre-bullseye AS clowder-runtime # environemnt variables ARG BRANCH="unknown" diff --git a/INSTALL.md b/INSTALL.md index ae9c8ba33..dc1c5e0ab 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -85,7 +85,7 @@ Next we install the script that will install or update clowder. cat > /home/browndog/update-clowder.sh << EOF #!/bin/bash -# CATS-WWW (master) is the main branch for this server +# CATS-WWW () is the main branch for this server # CATS-WWW1 (develop) if you want the latest version clowder_BRANCH=${clowder_BRANCH:-"CATS-WWW1"} diff --git a/app/controllers/Spaces.scala b/app/controllers/Spaces.scala index 1c197060a..9cb2fd4b8 100644 --- a/app/controllers/Spaces.scala +++ b/app/controllers/Spaces.scala @@ -7,6 +7,7 @@ import org.joda.time.DateTime import play.api.data.Forms._ import play.api.data.{Form, Forms} import play.api.i18n.Messages +import play.api.templates.HtmlFormat import play.api.{Logger, Play} import securesocial.core.providers.{Token, UsernamePasswordProvider} import services._ @@ -411,7 +412,7 @@ class Spaces @Inject() (spaces: SpaceService, users: UserService, events: EventS formData => { if (Permission.checkPermission(user, Permission.CreateSpace)) { Logger.debug("Creating space " + formData.name) - val newSpace = ProjectSpace(name = formData.name, description = formData.description, + val newSpace = ProjectSpace(name = HtmlFormat.escape(formData.name).toString(), description = HtmlFormat.escape(formData.description).toString(), created = new Date, creator = userId, homePage = formData.homePage, logoURL = formData.logoURL, bannerURL = formData.bannerURL, collectionCount = 0, datasetCount = 0, fileCount = 0, userCount = 0, spaceBytes = 0, metadata = List.empty, diff --git a/app/services/irods/IRODSByteStorageService.scala b/app/services/irods/IRODSByteStorageService.scala deleted file mode 100644 index e36952a86..000000000 --- a/app/services/irods/IRODSByteStorageService.scala +++ /dev/null @@ -1,198 +0,0 @@ -package services.irods - -import java.io._ -import java.security.{DigestInputStream, MessageDigest} - -import models.UUID -import org.apache.commons.codec.binary.Hex -import org.apache.commons.io.input.CountingInputStream -import org.irods.jargon.core.exception.JargonException -import org.irods.jargon.core.pub.io.IRODSFile -import play.api.{Logger, Play} -import play.api.Play._ -import services.ByteStorageService - -/** - * Helper to store data on disk. - * - */ -class IRODSByteStorageService extends ByteStorageService { - /** - * Save the bytes to IRODS - */ - def save(inputStream: InputStream, prefix: String, length: Long): Option[(String, Long)] = { - current.plugin[IRODSPlugin] match { - case None => { - Logger.error("No IRODSPlugin") - None - } - case Some(ipg) => { - var depth = Play.current.configuration.getInt("irods.depth").getOrElse(2) - - var relativePath = "" - var idstr = UUID.generate().stringify - // id seems to be same at the start but more variable at the end - while (depth > 0 && idstr.length > 4) { - depth -= 1 - if (relativePath == "") { - relativePath = idstr.takeRight(2) - } else { - relativePath += java.io.File.separatorChar + idstr.takeRight(2) - } - idstr = idstr.dropRight(2) - } - - // need to use whole id again, to make sure it is unique - relativePath += java.io.File.separatorChar + idstr - - // combine all pieces - val filePath = makePath(ipg.userhome, prefix, relativePath) - - // make sure the connection is open - if (!ipg.conn) { - ipg.openIRODSConnection() - } - - try { - // create folder structure - val file = ipg.getFileFactory().instanceIRODSFile(filePath) - if (!file.getParentFile.isDirectory && !file.getParentFile.mkdirs()) { - return None - } - - // fill a buffer Array - val cis = new CountingInputStream(inputStream) - val fos = ipg.getFileFactory().instanceIRODSFileOutputStream(file) - val buffer = new Array[Byte](16384) - var count: Int = -1 - while ( { - count = cis.read(buffer); count > 0 - }) { - fos.write(buffer, 0, count) - } - fos.close() - - val length = cis.getByteCount - - // finished - Some(relativePath, length) - } catch { - case e: JargonException => { - Logger.error("Could not save file " + filePath) - None - } - case e: IOException => { - Logger.error("Could not save file " + filePath) - None - } - } finally { - ipg.closeIRODSConnection() - } - } - } - } - - /** - * Get the bytes from IRODS - */ - def load(id: String, prefix: String): Option[InputStream] = { - current.plugin[IRODSPlugin] match { - case None => { - Logger.error("No IRODSPlugin") - None - } - case Some(ipg) => { - // combine all pieces - val filePath = makePath(ipg.userhome, prefix, id) - - // find actual file and delete it - if (!ipg.conn) { - ipg.openIRODSConnection() - } - try { - val file = ipg.getFileFactory().instanceIRODSFile(filePath) - val is = ipg.getFileFactory().instanceIRODSFileInputStream(file) - - // HACK with an intermediary buffer - works, no errors - // I could not pass the InputStream is directly to Some(InputStream, String, String, Long) because, - // I think the is.close does not propagate from File > downloads > Enumerator through the chain - // to the IRODSFileInputStream. Closing the stream here works. - val buffer = new ByteArrayOutputStream() - var count: Int = -1 - val data = new Array[Byte](16384) - - while ({count = is.read(data, 0, data.length); count > 0}) { - buffer.write(data, 0, count) - } - is.close() - - Some(new ByteArrayInputStream(buffer.toByteArray)) - } catch { - case e: JargonException => { - Logger.error("Could not retrieve file " + filePath) - None - } - case e: IOException => { - Logger.error("Could not retrieve file " + filePath) - None - } - } finally { - ipg.closeIRODSConnection() - } - } - } - } - - /** - * Delete actual bytes from IRODS - */ - def delete(id: String, prefix: String): Boolean = { - current.plugin[IRODSPlugin] match { - case None => { - Logger.error("No IRODSPlugin") - false - } - case Some(ipg) => { - // combine all pieces - val filePath = makePath(ipg.userhome, prefix, id) - - // find actual file and delete it - if (!ipg.conn) { - ipg.openIRODSConnection() - } - try { - val file = ipg.getFileFactory().instanceIRODSFile(filePath) - if (file.exists()) file.deleteWithForceOption() else true - } catch { - case e: JargonException => { - Logger.error("Could not delete file " + filePath) - false - } - case e: IOException => { - Logger.error("Could not delete file " + filePath) - false - } - } finally { - ipg.closeIRODSConnection() - } - } - } - } - - def makePath(root: String, prefix: String, relativePath: String) = { - // create absolute path - var filePath = if (root.last != IRODSFile.PATH_SEPARATOR_CHAR) { - root + IRODSFile.PATH_SEPARATOR - } else { - root - } - - // add the prefix - if (prefix != "") { - filePath += prefix + IRODSFile.PATH_SEPARATOR - } - - // finally create full path - filePath + relativePath - } -} diff --git a/app/services/irods/IRODSPlugin.scala b/app/services/irods/IRODSPlugin.scala deleted file mode 100644 index 4a4921009..000000000 --- a/app/services/irods/IRODSPlugin.scala +++ /dev/null @@ -1,93 +0,0 @@ -package services.irods - -import play.api.{ Play, Plugin, Logger, Application } -import org.irods.jargon.core.connection.IRODSAccount -import org.irods.jargon.core.pub.IRODSFileSystem -import org.irods.jargon.core.pub.io.IRODSFileFactory -import org.irods.jargon.core.exception.JargonException - -/** - * A concrete storage based on the iRODS file storage system. There are 7 - * required parameters to establish a connection to iRODS including a - * host, port, username, password, user home, zone, and default storage resource - * name. - * - * Setup iRODS connection using Jargon. - * - * @date 2014-08-18 - * - */ -class IRODSPlugin(app: Application) extends Plugin { - - var userhome: String = _ - - var account: IRODSAccount = _ - var irodsFileSystem: IRODSFileSystem = _ - var irodsFileFactory: IRODSFileFactory = _ - private var _conn: Boolean = false - - override def onStart() { - Logger.info("Starting iRODS Plugin.") - openIRODSConnection() - Logger.info("irods: Connected. " + conn) - } - - override def onStop() { - // close connection - closeIRODSConnection() - Logger.info("iRODSPlugin has stopped.") - } - - //Is the plugin enabled? - override def enabled = true - - - def openIRODSConnection() = { - lazy val configuration = Play.current.configuration - // you can now access the application.conf settings - - // seven required fields (similar to .irodsEnv file used in icommands client) - val host = configuration.getString("irods.host").getOrElse("") - val port = configuration.getInt("irods.port").getOrElse(0) - val username = configuration.getString("irods.username").getOrElse("") - val password = configuration.getString("irods.password").getOrElse("") - val zone = configuration.getString("irods.zone").getOrElse("") - val defaultStorageResource = configuration.getString("irods.defaultStorageResource").getOrElse("") - userhome = configuration.getString("irods.userhome").getOrElse("") - - val usercurrent = configuration.getString("irods.usercurrent").getOrElse("") - - try { - account = IRODSAccount.instance(host, port, username, password, userhome, zone, defaultStorageResource) - irodsFileSystem = IRODSFileSystem.instance() // RODSFileSystem shared object, initialized - Logger.debug("irods: Connecting to " + account.toString()) - - // the actual connection, For a given account creates an IRODSFileFactory that can return iRODS file objects for this particular connection. - irodsFileFactory = irodsFileSystem.getIRODSFileFactory(account) - - _conn = true - } catch { - case je: org.irods.jargon.core.exception.JargonException => Logger.error("irods: Error connecting to iRODS server. " + je.toString); _conn = false - case t: Throwable => Logger.error("irods: Unknown error connecting to iRODS server: " + t.toString); _conn = false - } - } - - def closeIRODSConnection() = { - Logger.info("irods: Closing connection.") - irodsFileSystem.closeAndEatExceptions() - _conn = false - } - - // Close the session that is connected to the particular iRODS server with the given account. - def closeIRODSAccountConnection() = { - Logger.info("irods: Closing account connection.") - irodsFileSystem.closeAndEatExceptions(account) - _conn = false - } - - //getters - def getFileFactory(): IRODSFileFactory = { return irodsFileFactory } - def conn = _conn - // setter - def conn_= (value:Boolean):Unit = _conn = value -} \ No newline at end of file diff --git a/app/views/home.scala.html b/app/views/home.scala.html index e45e4b2b1..69aaa20e6 100644 --- a/app/views/home.scala.html +++ b/app/views/home.scala.html @@ -439,7 +439,6 @@

@collectionInfo. core: { data: { url: function(node) { - console.log(node); return node.id === "#" ? "api/tree/getChildrenOfNode?nodeType=root" : "api/tree/getChildrenOfNode?nodeId="+node.id+"&nodeType="+node.data.type+"&role="+node.role+""; diff --git a/app/views/spaces/listItem.scala.html b/app/views/spaces/listItem.scala.html index fd96c1a53..474fe6fb2 100644 --- a/app/views/spaces/listItem.scala.html +++ b/app/views/spaces/listItem.scala.html @@ -22,7 +22,7 @@

@space.name

-
@Html(space.description.replace("\n","
"))
+
space.description.replace("\n","
")
@space.created.format("MMM dd, yyyy")
@space.datasetCount diff --git a/app/views/spaces/listSpaces.scala.html b/app/views/spaces/listSpaces.scala.html index 9f62efefb..5c3bd3e22 100644 --- a/app/views/spaces/listSpaces.scala.html +++ b/app/views/spaces/listSpaces.scala.html @@ -47,13 +47,15 @@

@title

case (_, _) => {} } } -
- -
+ @if(user.isDefined) { +
+ +
+ }