Commit f36fb643 authored by Benjamin Rokseth's avatar Benjamin Rokseth
Browse files

Merge branch 'release/0.6.1'

parents ac1d2dc5 466e12ab
......@@ -13,10 +13,21 @@ The resulting compose file can then be used with `docker-compose up -d` to provi
# Releases
## 0.6.1 (2016-11-22) extra feature
## 0.6.1 (2016-11-24) bugfixes/extras/reverts
KOHA: 2ad15d86c314e0ef882fb9cf1eb9515f98dacf2a
- patron-client:
- DEICH-173: Make search less strict
- temporarily disable due to instability:
- DEICH-445: mypage: add expected date
- DEICH-467: Add select to change pickup location
- catalinker:
- DEICH-460_erstatte_ui_blocker_med_spinnere
- DEICH-482 fix prefilling of predefined values
- DEICH-484: fix unwanted storing of suggested data
- services:
- DEICH-475: map 025$a as EAN of publication
## 0.6.0 (2016-11-22)
......
......@@ -177,7 +177,7 @@ services:
elasticsearch:
container_name: elasticsearch
image: digibib/elasticsearch:2.3.5
image: digibib/elasticsearch:6bf766734e7f82a6f0d4a406ca9a528a67c02801
networks:
- backend
ports:
......
......@@ -193,7 +193,7 @@ services:
elasticsearch:
container_name: elasticsearch
image: digibib/elasticsearch:2.3.5
image: digibib/elasticsearch:6bf766734e7f82a6f0d4a406ca9a528a67c02801
networks:
- backend
ports:
......
......@@ -146,7 +146,7 @@ services:
elasticsearch:
container_name: elasticsearch
image: digibib/elasticsearch:2.3.5
image: digibib/elasticsearch:6bf766734e7f82a6f0d4a406ca9a528a67c02801
networks:
- backend
volumes_from:
......
......@@ -2,7 +2,7 @@ export SIP_PORT=6001
export SIP_PROXY_PORT=6002
export SIP_AUTOPASS=secret
export SIP_AUTOPASS_ENCRYPTED=encryptme
export KOHA_IMAGE_TAG=3df1e1097b2fa4d2eae7766745fc172184d2644c
export KOHA_IMAGE_TAG=2ad15d86c314e0ef882fb9cf1eb9515f98dacf2a
export KOHA_ADMINUSER=admin
export KOHA_ADMINPASS=secret
export KOHA_INSTANCE=name
......
......@@ -625,6 +625,7 @@ body {
top: 32px;
display: block;
margin-top: -23px;
float: right;
}
.support-panel-expander {
display: inline-block;
......
......@@ -2177,13 +2177,11 @@
let keypath = this.getNodeInfo(node).resolve() + '.current.value'
var observer = ractive.observe(keypath, function (newvalue, oldValue) {
if (!setting) {
setting = true
window.setTimeout(function () {
$(node).val(newvalue).trigger('change')
setting = false
}, 0)
}
setting = true
window.setTimeout(function () {
$(node).val(newvalue).trigger('change')
setting = false
}, 0)
})
return {
......@@ -2862,7 +2860,7 @@
}
})
}
ractive.set(origin + '.searchResult.hidden', true)
ractive.set(`${origin}.searchResult.hidden`, true)
ractive.set(`${grandParentOf(event.keypath)}.showInputs`, event.index.inputValueIndex || 0)
let suggestedValuesGraphNode = ractive.get(`${grandParentOf(grandParentOf(event.keypath))}.suggestedValuesForNewResource.${event.index.inputValueIndex}`)
if (suggestedValuesGraphNode) {
......@@ -2874,7 +2872,7 @@
overrideMainEntry: true
}, suggestedValuesGraphNode, event.context.rdfType)
}
var searchTerm = ractive.get(origin + '.searchResult.searchTerm')
let searchTerm = ractive.get(`${origin}.searchResult.searchTerm`)
if (searchTerm) {
_.each(event.context.inputs, function (input) {
if (input.preFillFromSearchField) {
......@@ -2885,14 +2883,14 @@
}
},
createNewResource: function (event, origin) {
var maintenance = origin.indexOf('maintenanceInputs') !== -1
var displayValueInput = _.find(event.context.inputs, function (input) {
let maintenance = origin.indexOf('maintenanceInputs') !== -1
let displayValueInput = _.find(event.context.inputs, function (input) {
return input.displayValueSource === true
})
let searchOriginInput = ractive.get(grandParentOf(event.keypath))
let useAfterCreation = searchOriginInput.useAfterCreation
var setCreatedResourceUriInSearchInput = function (resourceUri) {
let setCreatedResourceUriInSearchInput = function (resourceUri) {
if (!maintenance) {
ractive.set(origin + '.current.value', resourceUri)
if (displayValueInput) {
......@@ -2910,33 +2908,35 @@
ractive.set(grandParentOf(event.keypath) + '.showInputs', null)
return resourceUri
}
var patchMotherResource = function (resourceUri) {
var targetInput = ractive.get(grandParentOf(origin))
let patchMotherResource = function (resourceUri) {
let targetInput = ractive.get(grandParentOf(origin))
if (!useAfterCreation && !targetInput.isSubInput) {
Main.patchResourceFromValue(ractive.get('targetUri.' + targetInput.rdfType), targetInput.predicate, ractive.get(origin), targetInput.datatypes[ 0 ], errors)
}
return resourceUri
}
var setCreatedResourceValuesInInputs = function (resourceUri) {
if (useAfterCreation) {
ractive.set('targetUri.' + event.context.rdfType, resourceUri)
var groupInputs = ractive.get('inputGroups')
_.each(event.context.inputs, function (input) {
_.each(groupInputs, function (group) {
_.each(group.inputs, function (groupInput) {
if (groupInput.predicate === input.predicate && groupInput.rdfType === input.rdfType) {
groupInput.values = deepClone(input.values)
}
})
function setCreatedResourceValuesInMainInputs () {
let groupInputs = ractive.get('inputGroups')
_.each(event.context.inputs, function (input) {
_.each(groupInputs, function (group) {
_.each(group.inputs, function (groupInput) {
if (groupInput.predicate === input.predicate && groupInput.rdfType === input.rdfType) {
ractive.set(`${groupInput.keypath}.values`, deepClone(input.values))
}
})
})
})
}
let setTargetUri = function (resourceUri) {
if (useAfterCreation) {
ractive.set('targetUri.' + event.context.rdfType, resourceUri)
updateInputsForDependentResources(event.context.rdfType, resourceUri)
ractive.update()
updateBrowserLocationWithUri(event.context.rdfType, resourceUri)
}
return resourceUri
}
var clearInputsAndSearchResult = function () {
let clearInputsAndSearchResult = function () {
if (ractive.get(origin + '.searchResult')) {
ractive.set(origin + '.searchResult', null)
}
......@@ -2946,15 +2946,18 @@
ractive.set(event.keypath + '.showInputs', false)
ractive.set(grandParentOf(origin) + '.allowAddNewButton', true)
}
var nop = function (uri) {
let nop = function (uri) {
return uri
}
let originTarget = $(`span[data-support-panel-base-id=support_panel_base_${ractive.get(origin).uniqueId}] span a.support-panel-expander`)
let wait = ractive.get('waitHandler').newWaitable(originTarget)
saveInputs(_.union(event.context.inputs, ractive.get(`${grandParentOf(grandParentOf(event.keypath))}.searchMainResource`) ? allTopLevelGroupInputsForDomain(event.context.rdfType) : []), event.context.rdfType)
if (!maintenance) {
setCreatedResourceValuesInMainInputs()
}
saveInputs(ractive.get(`${grandParentOf(grandParentOf(event.keypath))}.searchMainResource`) ? allTopLevelGroupInputsForDomain(event.context.rdfType) : event.context.inputs, event.context.rdfType)
.then(setCreatedResourceUriInSearchInput)
.then(!maintenance ? patchMotherResource : nop)
.then(!maintenance ? setCreatedResourceValuesInInputs : nop)
.then(!maintenance ? setTargetUri : nop)
.then(clearInputsAndSearchResult)
.then(wait.cancel)
},
......
......@@ -1494,6 +1494,12 @@ module.exports = (app) => {
predicate: 'agent',
enableCreateNewResource: true
},
{
resourceType: 'Serial',
wrappedIn: 'SerialIssue',
predicate: 'serial',
enableCreateNewResmource: true
},
{
resourceType: 'Person',
wrappedIn: '/Work',
......
......@@ -41,21 +41,42 @@ module.exports = (app) => {
})
app.put('/api/v1/holds', jsonParser, (request, response) => {
fetch(`http://xkoha:8081/api/v1/holds/${request.body.reserveId}`, {
method: 'PUT',
body: JSON.stringify({
branchcode: request.body.branchCode,
priority: 1
})
fetch('http://xkoha:8081/api/v1/holds', {
method: 'GET'
}).then(res => {
if (res.status === 200) {
response.sendStatus(200)
return res.json()
} else {
throw Error('Could not change pickup location')
}
}).catch(error => {
console.log(error)
response.sendStatus(500)
})
}).then(reserves => reserves.find(reserve => reserve.reserve_id === request.body.reserveId && reserve.borrowernumber === request.session.borrowerNumber))
.then(reserve => {
if (!reserve) {
return response.sendStatus(404)
}
const modifiedReserve = {}
if (request.body.branchCode) {
modifiedReserve.branchcode = request.body.branchCode
}
if (request.body.suspended) {
modifiedReserve.suspended = request.body.suspended ? 1 : 0
}
modifiedReserve.priority = Number(reserve.priority)
fetch(`http://xkoha:8081/api/v1/holds/${request.body.reserveId}`, {
method: 'PUT',
body: JSON.stringify(modifiedReserve)
}).then(res => {
if (res.status === 200) {
response.sendStatus(200)
} else {
response.sendStatus(500)
throw Error('Could not change pickup location')
}
})
})
.catch(error => {
console.log(error)
response.sendStatus(500)
})
})
}
......@@ -218,7 +218,7 @@ module.exports = (app) => {
returnVal = 12
}
return returnVal
return String(returnVal)
}
function getEligibleItems (items) {
......
......@@ -63,23 +63,33 @@ module.exports = (app) => {
itemResponses.forEach(itemResponse => {
const items = {}
itemResponse.items.forEach(item => {
if (item.status === 'Utilgjengelig') {
// Theese items should not count, or be processed further,
// as they are not available to end users.
return
}
const newItem = {
shelfmark: item.itemcallnumber,
status: item.status,
reservable: item.reservable === 1,
branchcode: item.homebranch,
barcode: item.barcode,
location: item.location
location: item.location,
notforloan: item.status === 'Ikke til hjemlån'
}
const key = `${newItem.branchcode}_${newItem.shelfmark}_${item.location}`
const key = `${newItem.branchcode}_${newItem.shelfmark}_${newItem.location}_${newItem.notforloan}`
if (!items[ key ]) {
newItem.available = 0
newItem.total = 0
items[ key ] = newItem
}
if (newItem.status === 'Ledig') {
if (newItem.status === 'Ledig' || newItem.notforloan) {
items[ key ].available++
}
items[ key ].total++
if (newItem.reservable) {
items[ key ].reservable = true
}
})
itemsByRecordId[ itemResponse.biblio.biblionumber ] = items
})
......
......@@ -29,10 +29,12 @@ class Libraries extends React.Component {
}
render () {
const { selectProps, selectedBranchCode, onChangeAction } = this.props
const { selectProps, selectedBranchCode, onChangeAction, disabled } = this.props
return (
<select ref={e => this.select = e} {...selectProps} defaultValue={selectedBranchCode}
onChange={onChangeAction ? this.handleSelectChange : undefined}>
<select ref={e => this.select = e} {...selectProps}
defaultValue={selectedBranchCode}
onChange={onChangeAction ? this.handleSelectChange : undefined}
disabled={disabled}>
{this.renderOptions()}
</select>
)
......@@ -44,7 +46,8 @@ Libraries.propTypes = {
selectProps: PropTypes.object,
selectedBranchCode: PropTypes.string,
reserveId: PropTypes.string,
onChangeAction: PropTypes.func
onChangeAction: PropTypes.func,
disabled: PropTypes.bool
}
export default Libraries
......@@ -36,7 +36,7 @@ class Publication extends React.Component {
const { publication, startReservation } = this.props
const languages = [ ...new Set(publication.languages.map(language => this.props.intl.formatMessage({ id: language }))) ]
const formats = [ ...new Set(publication.formats.map(format => this.props.intl.formatMessage({ id: format }))) ]
const publicationYearLanguageSeparator = function () {
const publicationYearLanguageSeparator = () => {
if (publication.publicationYear && publication.languages.length > 0) {
return ', '
}
......@@ -68,7 +68,7 @@ class Publication extends React.Component {
<span data-automation-id="publication_record_id">{publication.recordId}</span>
</div>
</div>
{publication.items.length > 0 ? (
{(publication.items.length > 0 && publication.items.filter(item => item.reservable).length > 0) ? (
<div className="reserve-button">
<ClickableElement onClickAction={startReservation} onClickArguments={publication.recordId}>
<button className="red-btn" type="button"
......
......@@ -20,13 +20,25 @@ import Subtitles from './work/fields/publication/Subtitles'
class PublicationInfo extends React.Component {
renderLocation (location) {
if (location !== '') {
if (location && location !== '') {
return <span className="item-location">{location}</span>
}
}
renderItems (items) {
if (items) {
items.sort((a, b) => {
// Sort items by branch, shelfmark, and "Ikke til hjemlån"-status
const keya = `${this.props.intl.formatMessage({ id: a.branchcode })}${a.shelfmark}${a.notforloan}`
const keyb = `${this.props.intl.formatMessage({ id: b.branchcode })}${b.shelfmark}${b.notforloan}`
if (keya > keyb) {
return 1
}
if (keya < keyb) {
return -1
}
return 0
})
return (
<NonIETransitionGroup
transitionName="fade-in"
......@@ -44,25 +56,36 @@ class PublicationInfo extends React.Component {
</tr>
</thead>
<tbody data-automation-id="work_items">
{items.map(item => (
<tr key={item.barcode} className={(item.available > 0) ? 'available' : 'not-available'}>
<td data-automation-id="item_location">{this.props.intl.formatMessage({ id: item.branchcode })}</td>
<td data-automation-id="item_shelfmark">{item.shelfmark} {this.renderLocation(item.location)}</td>
<td data-automation-id="item_status">{this.renderAvailability(item.available, item.total)}</td>
</tr>
))}
{items.map(item => {
return (
<tr key={item.barcode} className={(item.available > 0) ? 'available' : 'not-available'}>
<td data-automation-id="item_location">{this.props.intl.formatMessage({ id: item.branchcode })}</td>
<td data-automation-id="item_shelfmark">{item.shelfmark} {this.renderLocation(item.location)}</td>
<td data-automation-id="item_status">{this.renderAvailability(item)}</td>
</tr>
)
})}
</tbody>
</NonIETransitionGroup>
)
}
}
renderAvailability (available, total) {
return (
<span>
{available} <FormattedMessage {...messages.of} /> {total} <FormattedMessage {...messages.available} />
</span>
)
renderAvailability (item) {
if (item.notforloan) {
return (
<span>
{item.total} <FormattedMessage {...messages.available} /> - <FormattedMessage {...messages.onlyInhouse} />
</span>
)
} else {
return (
<span>
{item.available} <FormattedMessage {...messages.of} /> {item.total}
<FormattedMessage {...messages.available} />
</span>
)
}
}
render () {
......@@ -156,6 +179,11 @@ export const messages = defineMessages({
id: 'PublicationInfo.branch',
description: 'Table header for publication items table',
defaultMessage: 'Branch'
},
onlyInhouse: {
id: 'PublicationInfo.onlyInhouse',
description: 'Label stating that item can only be used in the library',
defaultMessage: 'Only for use in the library'
}
})
......
......@@ -76,12 +76,14 @@ class UserLoans extends React.Component {
<td data-automation-id="UserLoans_reservation_title">{item.title}</td>
<td data-automation-id="UserLoans_reservation_author">{item.author}</td>
<td data-automation-id="UserLoans_reservation_orderedDate">{formatDate(item.orderedDate)}</td>
<td data-automation-id="UserLoans_reservation_library">{this.renderLibrarySelect(item)}</td>
<td data-automation-id="UserLoans_reservation_library">{this.props.libraries[ item.branchCode ]}</td>
<td>
<span data-automation-id="UserLoans_reservation_queue_place">{item.queuePlace}</span>
<span>&nbsp;</span>
<span
data-automation-id="UserLoans_reservation_waitingPeriod">{this.renderWaitingPeriod(item.expected)}</span>
<span data-automation-id="UserLoans_reservation_queue_place">{item.queuePlace > 0
? item.queuePlace
: <FormattedMessage {...messages.enRoute} />}</span>
{/* <span>&nbsp;</span>
<span
data-automation-id="UserLoans_reservation_waitingPeriod">{this.renderWaitingPeriod(item.expected)}</span> */}
</td>
<td>
<ClickableElement onClickAction={this.props.reservationActions.startCancelReservation}
......@@ -132,10 +134,13 @@ class UserLoans extends React.Component {
<FormattedMessage {...messages.placeInQueue} />
</div>
<div className="meta-content">
<span data-automation-id="UserLoans_reservation_queue_place">{item.queuePlace}</span>
<span>&nbsp;</span>
<span
data-automation-id="UserLoans_reservation_waitingPeriod">{this.renderWaitingPeriod(item.expected)}</span>
data-automation-id="UserLoans_reservation_queue_place">{item.queuePlace > 0
? item.queuePlace
: <FormattedMessage {...messages.enRoute} />}</span>
{/* <span>&nbsp;</span>
<span
data-automation-id="UserLoans_reservation_waitingPeriod">{this.renderWaitingPeriod(item.expected)}</span> */}
</div>
</div>
<div className="meta-item">
......@@ -143,7 +148,7 @@ class UserLoans extends React.Component {
<FormattedMessage {...messages.pickupLocation} />
</div>
<div className="meta-content" data-automation-id="UserLoans_reservation_library">
{this.renderLibrarySelect(item)}
{this.props.libraries[ item.branchCode ]}
</div>
</div>
<div className="meta-item">
......@@ -165,12 +170,15 @@ class UserLoans extends React.Component {
renderLibrarySelect (item) {
return (
<div style={{ minWidth: '10em' }}>
{this.props.isRequestingChangePickupLocation
{this.props.isRequestingChangePickupLocation === item.reserveId
? <Loading />
: (
<div className="select-container">
<Libraries libraries={this.props.libraries} selectedBranchCode={item.branchCode}
onChangeAction={this.props.reservationActions.changePickupLocation} reserveId={item.reserveId} />
<Libraries libraries={this.props.libraries}
selectedBranchCode={item.branchCode}
disabled={this.props.isRequestingChangePickupLocation !== false}
onChangeAction={this.props.reservationActions.changePickupLocation}
reserveId={item.reserveId} />
</div>
)}
</div>)
......@@ -253,15 +261,9 @@ class UserLoans extends React.Component {
}
renderExpectedEstimationPrefix (estimate) {
if (estimate.includes('')) {
return (
<FormattedMessage {...messages.approx} />
)
} else {
return (
<FormattedMessage {...messages.moreThan} />
)
}
return estimate.includes('')
? <FormattedMessage {...messages.approx} />
: <FormattedMessage {...messages.moreThan} />
}
renderTabs () {
......@@ -303,7 +305,7 @@ UserLoans.propTypes = {
libraries: PropTypes.object.isRequired,
loanActions: PropTypes.object.isRequired,
reservationActions: PropTypes.object.isRequired,
isRequestingChangePickupLocation: PropTypes.bool.isRequired,
isRequestingChangePickupLocation: PropTypes.oneOfType([ PropTypes.bool, PropTypes.string ]).isRequired,
loansAndReservationError: PropTypes.object,
mediaQueryValues: PropTypes.object,
intl: intlShape.isRequired
......@@ -414,6 +416,11 @@ export const messages = defineMessages({
id: 'UserLoans.unknown',
description: 'Text displayed when unable to estimate waiting period',
defaultMessage: '(Unknown waiting period)'
},
enRoute: {
id: 'UserLoans.enRoute',
description: 'Text displayed when item is en route',
defaultMessage: 'En route'
}
})
......
......@@ -88,7 +88,7 @@ class RegistrationFormPartOne extends React.Component {
<form onSubmit={this.props.handleSubmit(this.props.registrationActions.checkForExistingUser)}>
<h1><FormattedMessage {...messages.registerAsLoaner} /></h1>
{/* IE11-hack begin: Make the first fieldset with firstName and lastName display with correct width */}
{ isIe() ? <fieldset style={{ visibility: 'hidden' }}><input /></fieldset> : null }
{isIe() ? <fieldset style={{ visibility: 'hidden' }}><input /></fieldset> : null}
{/* IE11-hack end */}
<fieldset disabled={this.props.checkForExistingUserSuccess}>
<legend><FormattedMessage {...messages.nameLabel} /></legend>
......
......@@ -30,9 +30,8 @@ export default {
'ContentAdaptations.labelContentAdaptations': 'Tilrettelagt innhold',
'DeweyNumber.labelDeweyNumber': 'Deweynr.',
'Duration.duration': 'Spilletid',