Commit 157f09d1 authored by laurent l's avatar laurent l

Add cancel hold action for Bokeh

parent c46e6812
......@@ -16,7 +16,8 @@ import {
WrongLoginPassword,
Cookies,
Manager,
Iso8601Date
Iso8601Date,
PortalActionResult
} from '../models/';
import { RadSideDrawer } from 'nativescript-ui-sidedrawer';
......@@ -381,6 +382,30 @@ export class LibraryController extends Controller {
}
public cancelHoldAction(args) {
const hold = <Hold>args.object.bindingContext;
this.dialogs
.confirm({
title: hold.getTitle(),
message: L('confirm_cancel_hold'),
okButtonText: L('OK'),
cancelButtonText: L('cancel')
})
.then((result) => {
if (!result) return;
return this._portal
.cancelHold(hold.getAccount(), hold)
.then((result: PortalActionResult) => {
if (result.isSuccess()) {
Database.current().delete(hold);
this._loadLoansAndHolds()
}
})
})
}
protected _authenticationDone(account: Account): LibraryController {
Database.current().save(account);
this.set('busy', true);
......
import {ItemOperation, Serializable} from '../models';
import { ItemOperation, Serializable } from '../models';
export class Hold extends ItemOperation {
protected _hold_id: string;
protected _status: string;
public setHoldId(hold_id: string): this {
this._hold_id = hold_id;
return this;
......@@ -33,11 +33,10 @@ export class Hold extends ItemOperation {
.set('status', this._status);
}
public materializeFrom(serializable: Serializable) {
super.materializeFrom(serializable);
this._hold_id = serializable.get('hold_id');
this._status = serializable.get('status');
}
}
export { Persistable, Serializable, DataSource } from './persistable';
export { Database } from './database';
export { Account } from './account';
export { ItemOperation } from './item-operation';
export { Loan } from './loan';
export { Hold } from './hold';
export { Portal } from './portal';
export { HTTPClient } from './http/http-client';
export { NSHTTPClient } from './http/ns-http-client';
export { NullHTTPClient } from './http/null-http-client';
export { PortalAdapter } from './portal/adapter';
export { OAuthTokenNotSet } from './portal/oauth-token-not-set';
export { WrongLoginPassword } from './portal/wrong-login-password';
export { PortalDetectionFail } from './portal/portal-detection-fail';
export { Cookies } from './cookies/cookies';
export { Manager, MBPortal } from './manager';
export { Record } from './record';
export { Item } from './item';
export { Novelties } from './novelties';
export { Card } from './card';
export { Iso8601Date } from './iso8601_date';
export { BackgroundJobs } from './background-jobs/background-jobs';
export { Form } from './form';
export { Persistable, Serializable, DataSource } from './persistable'
export { Database } from './database'
export { Account } from './account'
export { ItemOperation } from './item-operation'
export { Loan } from './loan'
export { Hold } from './hold'
export { Portal } from './portal'
export { HTTPClient } from './http/http-client'
export { NSHTTPClient } from './http/ns-http-client'
export { NullHTTPClient } from './http/null-http-client'
export { PortalAdapter } from './portal/adapter'
export { OAuthTokenNotSet } from './portal/oauth-token-not-set'
export { WrongLoginPassword } from './portal/wrong-login-password'
export { PortalDetectionFail } from './portal/portal-detection-fail'
export { PortalActionResult } from './portal/portal-action-result'
export { Cookies } from './cookies/cookies'
export { Manager, MBPortal } from './manager'
export { Record } from './record'
export { Item } from './item'
export { Novelties } from './novelties'
export { Card } from './card'
export { Iso8601Date } from './iso8601_date'
export { BackgroundJobs } from './background-jobs/background-jobs'
export { Form } from './form'
......@@ -45,15 +45,18 @@ export abstract class Persistable extends Observable {
export interface Serializable {
interface Serializable {
set(name, value): Serializable;
get(name, modelClass?: any): any;
serialize(): Object;
}
export interface DataSource {
interface DataSource {
delete(model: Persistable): DataSource;
save(model: Persistable): DataSource;
findAll(modelClass: any): any;
}
export { DataSource, Serializable }
import * as PortalAdapters from './portal/index';
import {PortalAdapter} from './portal/adapter';
import {Database, Account, Item} from '../models';
import { PortalAdapter } from './portal/adapter';
import { Database, Account, Hold, Item } from '../models';
export class Portal {
protected _adapters: Map<string, PortalAdapter> = new Map<string, PortalAdapter>();
get adapters(): Map<string, PortalAdapter> {
return this._adapters.size > 0
? this._adapters
......@@ -39,7 +40,7 @@ export class Portal {
.searchUrl(account, terms);
}
public canFetchNovelties(account: Account): boolean {
return this
.findAdapterIdentifiedBy(account.getAdapterIdentifier())
......@@ -68,10 +69,17 @@ export class Portal {
}
public cancelHold(account: Account, hold: Hold): Promise<any> {
return this
.findAdapterIdentifiedBy(account.getAdapterIdentifier())
.cancelHold(account, hold)
}
public updateAccountInfo(account: Account): Promise<any> {
return this
.findAdapterIdentifiedBy(account.getAdapterIdentifier())
.updateCard(account).then( () => {
.updateCard(account).then(() => {
Database.current().save(account)
return account
});
......@@ -79,7 +87,7 @@ export class Portal {
protected _populateAdapters(): Map<string, PortalAdapter> {
for(let key in PortalAdapters) {
for (let key in PortalAdapters) {
let adapter: PortalAdapter = new PortalAdapters[key]();
this._adapters.set(adapter.getIdentifier(), adapter);
}
......
import * as http from 'tns-core-modules/http';
import * as http from 'tns-core-modules/http'
import { ImageSource } from 'tns-core-modules/image-source'
import {
Cookies,
......@@ -9,116 +9,122 @@ import {
Loan,
Item,
Form,
Hold
} from '../../models';
Hold,
PortalActionResult
} from '../../models'
export abstract class PortalAdapter {
private _http: any;
private _cookies: Cookies;
private _http: any
private _cookies: Cookies
public constructor() {
this._http = new NSHTTPClient();
this._cookies = new Cookies();
this._http = new NSHTTPClient()
this._cookies = new Cookies()
}
public setHTTP(http: any): PortalAdapter {
this._http = http;
return this;
this._http = http
return this
}
public updateAdapterHTTPClient(adapter: PortalAdapter): PortalAdapter {
adapter.setHTTP(this._http);
return this;
adapter.setHTTP(this._http)
return this
}
public signIn(account): Promise<any> {
this._cookies.clearCookies();
this._cookies.clearCookies()
return this
._signIn(account)
.then(() => {
return this.updateAccountInfo(account);
});
return this.updateAccountInfo(account)
})
}
public refresh(account): Promise<any> {
if (!account.isConnected()) {
account.setCookies(new Array<any>());
account.setCookies(new Array<any>())
return this._signIn(account).then(() => {
return this.refresh(account);
});
return this.refresh(account)
})
}
let db: Database = Database.current();
let db: Database = Database.current()
return this
._fetchLoans(account)
.then((loans) => {
account.deleteLoans(db);
account.deleteLoans(db)
loans.forEach((loan) => {
db.save(loan.setAccount(account));
});
db.save(loan.setAccount(account))
})
db.save(account);
db.save(account)
})
.then(() => {
return this._fetchHolds(account);
return this._fetchHolds(account)
})
.then((holds) => {
account.deleteHolds(db);
account.deleteHolds(db)
holds.forEach((hold) => {
db.save(hold.setAccount(account));
});
db.save(hold.setAccount(account))
})
account.setNumberOfHolds(holds.length);
db.save(account);
account.setNumberOfHolds(holds.length)
db.save(account)
})
}
public request(account: Account, options: any): Promise<http.HttpResponse> {
this._cookies.setCookies(account.getCookies());;
this._cookies.setCookies(account.getCookies())
return this._http
.request(options)
.then((response) => {
account.setCookies(this._cookies.getCookies());
return response;
});
account.setCookies(this._cookies.getCookies())
return response
})
}
public searchUrl(account: Account, terms: string): Promise<string> {
return Promise.resolve('');
return Promise.resolve('')
}
public canSearch(): boolean {
return false;
return false
}
public canFetchNovelties(): boolean {
return false;
return false
}
public fetchNovelties(account: Account, page: number): Promise<any> {
return Promise.resolve(new Array());
return Promise.resolve(new Array())
}
public updateCard(account: Account): Promise<any> {
return Promise.resolve();
return Promise.resolve()
}
public cancelHold(account: Account, hold: Hold): Promise<PortalActionResult> {
return Promise.reject(new PortalActionResult)
}
......@@ -127,37 +133,37 @@ export abstract class PortalAdapter {
? Promise.resolve()
: this._fetchAccountLabel(account).then(
(label) => {
account.setLabel(label);
});
account.setLabel(label)
})
}
public fetchItem(account, barcode): Promise<Item> {
return Promise.reject(null);
return Promise.reject(null)
}
protected _fetchAccountLabel(account: Account): Promise<any> {
return Promise.resolve(account.credentials['login']);
};
return Promise.resolve(account.credentials['login'])
}
protected _fetchHolds(account: Account): Promise<Array<Hold>> {
return Promise.resolve(new Array<Hold>());
return Promise.resolve(new Array<Hold>())
}
protected _fetchThumbnails<T extends ItemOperation>(account: Account,
items: Array<T>): Promise<Array<T>> {
return Promise.all(items.map((item) => {
return this._fetchThumbnail(account, item);
return this._fetchThumbnail(account, item)
}))
}
protected _fetchThumbnail<T extends ItemOperation>(account: Account, item: T): Promise<T> {
if (!item.getRecordThumbnail())
return Promise.resolve(item);
return Promise.resolve(item)
return this
.request(account,
......@@ -179,7 +185,7 @@ export abstract class PortalAdapter {
return response.content
.toImage()
.then((image_source: ImageSource) => {
return item.setRecordThumbnail('data:image/jpg;base64,' + image_source.toBase64String('jpg'));
return item.setRecordThumbnail('data:image/jpg;base64,' + image_source.toBase64String('jpg'))
})
})
}
......@@ -189,15 +195,15 @@ export abstract class PortalAdapter {
return new Form(form).toUrlEncodedString()
}
protected abstract _signIn(account: Account): Promise<any>;
protected abstract _signIn(account: Account): Promise<any>
public abstract getIdentifier(): string;
public abstract getIdentifier(): string
public abstract getLabel(): string;
public abstract getLabel(): string
public abstract canHandleWebsite($): boolean;
public abstract canHandleWebsite($): boolean
protected abstract _fetchLoans(account: Account): Promise<Array<Loan>>;
protected abstract _fetchLoans(account: Account): Promise<Array<Loan>>
}
import { PortalAdapter } from './adapter';
import { Database, Account, Hold, Loan, Record, Item } from '../../models';
import { Database, Account, Hold, Loan, Record, Item, PortalActionResult } from '../../models';
import { OAuthTokenNotSet } from './oauth-token-not-set';
......@@ -52,6 +52,18 @@ export class Bokeh extends PortalAdapter {
}
public cancelHold(account: Account, hold: Hold): Promise<PortalActionResult> {
return this
.requestUser(account, 'cancel-hold/id/' + hold.getHoldId())
.then((datas) => {
if (datas['status'] == 'canceled')
return new PortalActionResult().beSuccess()
return new PortalActionResult().beError(datas['error']);
})
}
public fetchNovelties(account: Account, page: number): Promise<any> {
return this
.request(account,
......
export {Bokeh} from './bokeh';
export {PMB} from './pmb';
export {OrpheeMedia} from './orpheemedia';
export {DecalogOpac} from './decalog-opac';
export {DecalogOpacV2} from './decalog-opac-v2';
export {Ermes} from './ermes';
export {Koha} from './koha';
export {KohaCas} from './koha-cas';
export {InMedia} from './inmedia';
export {Symphony} from './symphony';
export {InMediaV2} from './inmedia-v2';
export {IguanaV3} from './iguana-v3';
export {Autodetect} from './autodetect';
export { Bokeh } from './bokeh';
export { PMB } from './pmb';
export { OrpheeMedia } from './orpheemedia';
export { DecalogOpac } from './decalog-opac';
export { DecalogOpacV2 } from './decalog-opac-v2';
export { Ermes } from './ermes';
export { Koha } from './koha';
export { KohaCas } from './koha-cas';
export { InMedia } from './inmedia';
export { Symphony } from './symphony';
export { InMediaV2 } from './inmedia-v2';
export { IguanaV3 } from './iguana-v3';
export { Autodetect } from './autodetect';
export class PortalActionResult {
static readonly SUCCESS: string = 'SUCCESS'
static readonly ERROR: string = 'ERROR'
static readonly UNSUPPORTED: string = 'UNSUPPORTED'
protected _status: string = PortalActionResult.UNSUPPORTED
protected _message: string
public beSuccess(): this {
this._status = PortalActionResult.SUCCESS
return this
}
public beError(message: string): this {
this._status = PortalActionResult.ERROR
this._message = message
return this
}
public isSuccess(): boolean {
return this._status == PortalActionResult.SUCCESS
}
}
......@@ -8,7 +8,7 @@
pullToRefreshInitiated="{{ refreshLoansAndHoldsAction }}">
<lv:RadListView.itemTemplate>
<StackLayout class="list-item">
<GridLayout columns="120, *" rows="auto,auto,auto,auto,auto,auto" class="hold">
<GridLayout columns="120, *" rows="auto,auto,auto,auto,auto,auto,auto" class="hold">
<Label text="{{ getTitle() }}"
textWrap="true" col="0" row="0" colSpan="3"
class="title" />
......@@ -18,7 +18,7 @@
col="0"
row="1"
stretch="aspectFill"
rowSpan="4"
rowSpan="6"
height="160"
class="cover"/>
......@@ -36,7 +36,17 @@
row="{{ getRecordThumbnail().length ? '4' : '3' }}"
class="value" />
<Label text="{{ getStatus() }}" col="1" row="5" class="status" />
<Label text="{{ getStatus() }}"
col="1"
row="5"
class="status"
textWrap="true" />
<Button text="{{ '&#xf00d; ' + L('cancel') }}"
tap="{{ cancelHoldAction }}"
col="1" row="6"
class="bt_delete"/>
</GridLayout>
</StackLayout>
</lv:RadListView.itemTemplate>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment