Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ jobs:
- uses: actions/checkout@v2

- name: openapi-lint
uses: mhiew/redoc-lint-github-action@v2
uses: mbowman100/swagger-validator-action@master
with:
args: 'public/swagger.yml --skip-rule operation-operationId'
files: public/swagger.yml
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ 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.20.2 - 2022-04-30

### Fixed
- swagger lint action
- When downloading a file with a `'` in the name it would save the file as blob
- Fix for a rare race condition with masonry where tiles could end up overlapping in space page.
- Fixes bug where same extractor shows up multiple times and all Clowder instances index db on reindex [#327](https://github.com/clowder-framework/clowder/issues/327)

### Changed
- Changed `Enabled By SuperAdmin` to read `Enabled by Server Admin` [#344](https://github.com/clowder-framework/clowder/issues/344)

## 1.20.1 - 2022-04-04

### Fixed
Expand Down
3 changes: 2 additions & 1 deletion app/services/mongodb/MongoDBQueueService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ trait MongoDBQueueService {

// start pool to being processing queue actions
def listen() = {
if (queueTimer == null) {
//only if this is the primary clowder instance
if (queueTimer == null && configuration.getBoolean("clowder.primary").getOrElse(true)) {
// TODO: Need to make these in a separate pool
queueTimer = Akka.system().scheduler.schedule(0 seconds, 5 millis) {
getNextQueuedAction match {
Expand Down
39 changes: 21 additions & 18 deletions app/services/rabbitmq/RabbitMQMessageService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -128,24 +128,27 @@ class RabbitMQMessageService extends MessageService {
new MsgConsumer(channel.get, event_filter.get)
)

// Start actor to listen to extractor heartbeats
Logger.info("Starting extractor heartbeat listener")
// create fanout exchange if it doesn't already exist
channel.get.exchangeDeclare("extractors", "fanout", true)
// anonymous queue
val heartbeatsQueue = channel.get.queueDeclare().getQueue
// bind queue to exchange
channel.get.queueBind(heartbeatsQueue, "extractors", "*")
extractorsHeartbeats = Some(Akka.system.actorOf(
Props(new ExtractorsHeartbeats(channel.get, heartbeatsQueue)), name = "ExtractorsHeartbeats"
))
Logger.debug("Initializing a MsgConsumer for the ExtractorsHeartbeats")
channel.get.basicConsume(
heartbeatsQueue,
false, // do not auto ack
"ExtractorsHeartbeats", // tagging the consumer is important if you want to stop it later
new MsgConsumer(channel.get, extractorsHeartbeats.get)
)
//register new extractor only if this is the primary clowder instance
if (configuration.getBoolean("clowder.primary").getOrElse(true)) {
// Start actor to listen to extractor heartbeats
Logger.info("Starting extractor heartbeat listener")
// create fanout exchange if it doesn't already exist
channel.get.exchangeDeclare("extractors", "fanout", true)
// anonymous queue
val heartbeatsQueue = channel.get.queueDeclare().getQueue
// bind queue to exchange
channel.get.queueBind(heartbeatsQueue, "extractors", "*")
extractorsHeartbeats = Some(Akka.system.actorOf(
Props(new ExtractorsHeartbeats(channel.get, heartbeatsQueue)), name = "ExtractorsHeartbeats"
))
Logger.debug("Initializing a MsgConsumer for the ExtractorsHeartbeats")
channel.get.basicConsume(
heartbeatsQueue,
false, // do not auto ack
"ExtractorsHeartbeats", // tagging the consumer is important if you want to stop it later
new MsgConsumer(channel.get, extractorsHeartbeats.get)
)
}

// Setup Actor to submit new extractions to broker
extractQueue = Some(Akka.system.actorOf(Props(new PublishDirectActor(channel = channel.get,
Expand Down
39 changes: 11 additions & 28 deletions app/util/FileUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -856,37 +856,20 @@ object FileUtils {
//Download CONTENT-DISPOSITION encoding
//
def encodeAttachment(filename: String, userAgent: String) : String = {
val filenameStar = if (userAgent.indexOf("MSIE") > -1) {
URLEncoder.encode(filename, "UTF-8")
} else if (userAgent.indexOf("Edge") > -1){
MimeUtility.encodeText(filename
.replaceAll(",","%2C")
.replaceAll("\"","%22")
.replaceAll("/","%2F")
.replaceAll("=","%3D")
.replaceAll("&","%26")
.replaceAll(":","%3A")
.replaceAll(";","%3B")
.replaceAll("\\?","%3F")
.replaceAll("\\*","%2A")
val filenameStar = MimeUtility.encodeText(filename
.replaceAll("%","%25")
.replaceAll(" ","%20")
.replaceAll("\"","%22")
.replaceAll("'","%27")
.replaceAll(",","%2C")
.replaceAll("/","%2F")
.replaceAll("=","%3D")
.replaceAll(":","%3A")
.replaceAll(";","%3B")
.replaceAll("\\*","%2A")
,"utf-8","Q")
} else {
MimeUtility.encodeText(filename
.replaceAll("%","%25")
.replaceAll(" ","%20")
.replaceAll("\"","%22")
.replaceAll(",","%2C")
.replaceAll("/","%2F")
.replaceAll("=","%3D")
.replaceAll(":","%3A")
.replaceAll(";","%3B")
.replaceAll("\\*","%2A")
,"utf-8","Q")
}
Logger.debug(userAgent + ": " + filenameStar)

//Return the complete attachment header info
"attachment; filename*=UTF-8''" + filenameStar
}

}
2 changes: 1 addition & 1 deletion app/views/spaces/space.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ <h3>@Messages("collections.title")</h3>
match.masonry();

// layout Masonry again after all images have loaded
imagesLoaded( '.tiled-image', function() {
imagesLoaded( match, function() {
match.masonry({
itemSelector: '.tiled-image',
columnWidth: '.post-box',
Expand Down
4 changes: 2 additions & 2 deletions app/views/spaces/updateExtractors.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ <h1>Space Extractors</h1>
<th>Name</th>
<th>Description</th>
<th>Process Triggers</th>
<th class="text-center" title="Extractors marked for execution in all spaces">Enabled By SuperAdmin</th>
<th class="text-center" title="Follow selection in 'Enabled By SuperAdmin' column">Use Default</th>
<th class="text-center" title="Extractors marked for execution in all spaces">Enabled by Server Admin</th>
<th class="text-center" title="Follow selection in 'Enabled by Server Admin' column">Use Default</th>
<th class="text-center" title="Always trigger execution in this space">Enable in Space</th>
<th class="text-center" title="Never trigger execution in this space">Disable in Space</th>
</tr>
Expand Down
7 changes: 7 additions & 0 deletions conf/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,13 @@ api.version="beta"
# mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
mongodbURI = "mongodb://127.0.0.1:27017/clowder"

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Clowder Primary Instance
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# This is required if there are multiple clowder instances. This config variable indicates
# the primary clowder instance who takes care of special actions like registering new extractors,
# indexing db on a reindex etc. The default value is true which means only one instance of clowder running.
clowder.primary=true

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# RabbitMQ
Expand Down
2 changes: 1 addition & 1 deletion doc/src/sphinx/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
author = 'Luigi Marini'

# The full version, including alpha/beta/rc tags
release = '1.20.1'
release = '1.20.2'


# -- General configuration ---------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions docker/custom.conf
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ smtp.host=${?SMTP_SERVER}
service.byteStorage=services.filesystem.DiskByteStorageService
service.byteStorage=${?CLOWDER_STORAGE}

#primary Clowder instance
clowder.primary=true
clowder.primary=${?CLOWDER_PRIMARY}

# location in case of services.filesystem.DiskByteStorageService
clowder.diskStorage.path="/home/clowder/data"
clowder.diskStorage.path=${?CLOWDER_DISKPATH}
Expand Down
2 changes: 1 addition & 1 deletion project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import NativePackagerKeys._
object ApplicationBuild extends Build {

val appName = "clowder"
val version = "1.20.1"
val version = "1.20.2"
val jvm = "1.7"

def appVersion: String = {
Expand Down
2 changes: 1 addition & 1 deletion public/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ info:
Clowder is a customizable and scalable data management system to support any
data format and multiple research domains. It is under active development
and deployed for a variety of research projects.
version: 1.20.1
version: 1.20.2
termsOfService: https://clowder.ncsa.illinois.edu/clowder/tos
contact:
name: Clowder
Expand Down