View | Details | Raw Unified | Return to bug 14656
Collapse All | Expand All

(-)a/Koha/REST/V1/Biblios.pm (+28 lines)
Line 0 Link Here
1
package Koha::REST::V1::Biblios;
2
3
use Modern::Perl;
4
5
use Mojo::Base 'Mojolicious::Controller';
6
7
use Koha::Database;
8
9
sub delete_biblio {
10
    my ($c, $args, $cb) = @_;
11
12
    my $schema = Koha::Database->new->schema;
13
14
    my $biblio = $schema->resultset('Biblio')->find({biblionumber => $args->{biblionumber}});
15
    unless ($biblio) {
16
        return $c->$cb({error => "Biblio not found"}, 404);
17
    }
18
19
    my $itemCount = $schema->resultset('Item')->search({biblionumber => $args->{biblionumber}})->count();
20
    if ($itemCount) {
21
        return $c->$cb({error => "Biblio has Items attached. Delete them first."}, 400);
22
    }
23
24
    $biblio->delete();
25
    return $c->$cb('', 204);
26
}
27
28
1;
(-)a/api/v1/swagger.json (+50 lines)
Lines 14-19 Link Here
14
  },
14
  },
15
  "basePath": "/api/v1",
15
  "basePath": "/api/v1",
16
  "paths": {
16
  "paths": {
17
    "/biblios/{biblionumber}": {
18
      "delete": {
19
        "x-mojo-controller": "Koha::REST::V1::Biblios",
20
        "x-koha-permission": {
21
          "editcatalogue": "delete_catalogue"
22
        },
23
        "operationId": "deleteBiblio",
24
        "tags": ["biblios"],
25
        "summary": "Deletes the given Bibliographic Record",
26
        "description": "Can fail if there are dependecies to the Biblio.",
27
        "produces": [
28
          ""
29
        ],
30
        "parameters": [
31
          {
32
            "$ref": "#/parameters/biblionumberPathParam"
33
          }
34
        ],
35
        "responses": {
36
          "204": {
37
            "description": "Deleting the Record succeeded.",
38
            "schema": {
39
              "type": "string"
40
            }
41
          },
42
          "404": {
43
            "description": "No such Bibliographic record found",
44
            "schema": {
45
              "$ref": "#/definitions/error"
46
            }
47
          },
48
          "400": {
49
            "description": "Cannot delete the Bibliographic Record due to constraints. A constraint can be for example the presence of dependant Items.",
50
            "schema": {
51
              "$ref": "#/definitions/error"
52
            }
53
          }
54
        },
55
        "security": [
56
          { "multi_key_auth": [] }
57
        ]
58
      }
59
    },
17
    "/borrowers": {
60
    "/borrowers": {
18
      "get": {
61
      "get": {
19
        "x-mojo-controller": "Koha::REST::V1::Borrowers",
62
        "x-mojo-controller": "Koha::REST::V1::Borrowers",
Lines 459-464 Link Here
459
    }
502
    }
460
  },
503
  },
461
  "parameters": {
504
  "parameters": {
505
    "biblionumberPathParam": {
506
      "name": "biblionumber",
507
      "in": "path",
508
      "description": "Internal biblio identifier",
509
      "required": true,
510
      "type": "integer"
511
    },
462
    "borrowernumberPathParam": {
512
    "borrowernumberPathParam": {
463
      "name": "borrowernumber",
513
      "name": "borrowernumber",
464
      "in": "path",
514
      "in": "path",
(-)a/installer/data/mysql/atomicupdate/Bug14656-DeleteBibRecordsRESTAPI+Permission.pl (+33 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright Open Source Freedom Fighters
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
use C4::Context;
21
use Koha::AtomicUpdater;
22
23
my $dbh = C4::Context->dbh();
24
my $atomicUpdater = Koha::AtomicUpdater->new();
25
26
unless($atomicUpdater->find('Bug14656')) {
27
28
    use Koha::Auth::PermissionManager;
29
    my $pm = Koha::Auth::PermissionManager->new();
30
    $pm->addPermission({module => 'editcatalogue', code => 'delete_catalogue', description => "Allow deleting bibliographic records"});
31
32
    print "Upgrade done (Bug 14656: Add biblio delete (delete_catalogue) permission.)\n";
33
}
(-)a/installer/data/mysql/en/mandatory/userpermissions.sql (+1 lines)
Lines 13-18 INSERT INTO permissions (module, code, description) VALUES Link Here
13
   ( 'reserveforothers','place_holds', 'Place holds for patrons'),
13
   ( 'reserveforothers','place_holds', 'Place holds for patrons'),
14
   ( 'reserveforothers','modify_holds_priority', 'Modify holds priority'),
14
   ( 'reserveforothers','modify_holds_priority', 'Modify holds priority'),
15
   ( 'editcatalogue',   'edit_catalogue', 'Edit catalog (Modify bibliographic/holdings data)'),
15
   ( 'editcatalogue',   'edit_catalogue', 'Edit catalog (Modify bibliographic/holdings data)'),
16
   ( 'editcatalogue',   'delete_catalogue', 'Allow deleting bibliographic records'),
16
   ( 'editcatalogue',   'fast_cataloging', 'Fast cataloging'),
17
   ( 'editcatalogue',   'fast_cataloging', 'Fast cataloging'),
17
   ( 'editcatalogue',   'edit_items', 'Edit items'),
18
   ( 'editcatalogue',   'edit_items', 'Edit items'),
18
   ( 'editcatalogue',   'edit_items_restricted', 'Limit item modification to subfields defined in the SubfieldsToAllowForRestrictedEditing preference (please note that edit_item is still required)'),
19
   ( 'editcatalogue',   'edit_items_restricted', 'Limit item modification to subfields defined in the SubfieldsToAllowForRestrictedEditing preference (please note that edit_item is still required)'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/cat-toolbar.inc (-9 / +31 lines)
Lines 27-33 Link Here
27
	function addToShelf() {	window.open('/cgi-bin/koha/virtualshelves/addbybiblionumber.pl?biblionumber=[% biblionumber %]','Add_to_virtualshelf','width=500,height=400,toolbar=false,scrollbars=yes');
27
	function addToShelf() {	window.open('/cgi-bin/koha/virtualshelves/addbybiblionumber.pl?biblionumber=[% biblionumber %]','Add_to_virtualshelf','width=500,height=400,toolbar=false,scrollbars=yes');
28
	}
28
	}
29
    function printBiblio() {window.print(); }
29
    function printBiblio() {window.print(); }
30
[% IF CAN_user_editcatalogue_edit_catalogue or ( frameworkcode == 'FA' and CAN_user_editcatalogue_fast_cataloging ) %]
30
[% IF CAN_user_editcatalogue_edit_catalogue or CAN_user_editcatalogue_delete_catalogue or ( frameworkcode == 'FA' and CAN_user_editcatalogue_fast_cataloging ) %]
31
    function confirm_deletion() {
31
    function confirm_deletion() {
32
        var count = [% count %];
32
        var count = [% count %];
33
        var holdcount = [% holdcount %];
33
        var holdcount = [% holdcount %];
Lines 60-71 Link Here
60
            if ( count > 0 || holdcount > 0 ){
60
            if ( count > 0 || holdcount > 0 ){
61
                return false;
61
                return false;
62
            } else {
62
            } else {
63
                window.location="/cgi-bin/koha/cataloguing/addbiblio.pl?op=delete&biblionumber=[% biblionumber %]";
63
                deleteBiblio([% biblionumber %]);
64
            }
64
            }
65
	} else {
65
	} else {
66
            return false;
66
            return false;
67
	}
67
	}
68
    }
68
    }
69
	function deleteBiblio (biblionumber) {
70
		$.ajax({
71
		  "cache": false,
72
		  "headers": {},
73
		  "method": "delete",
74
		  "type":"delete",
75
		  "url": "/api/v1/biblios/"+biblionumber,
76
		  "dataType": "json",
77
		  "success": function (data, textStatus, jqXHR) {
78
			window.location="/cgi-bin/koha/catalogue/search.pl";
79
		  },
80
		  "error": function (jqXHR, textStatus, errorThrown) {
81
			var errorObject = $.parseJSON( jqXHR.responseText );
82
			if (!errorObject || !errorObject.error) {
83
			  alert(jqXHR.responseText);
84
			}
85
			else {
86
			  alert(errorObject.error);
87
			}
88
		  }
89
		});
90
	}
69
[% END %]
91
[% END %]
70
92
71
[% IF CAN_user_editcatalogue_edit_items or ( frameworkcode == 'FA' and CAN_user_editcatalogue_fast_cataloging ) %]
93
[% IF CAN_user_editcatalogue_edit_items or ( frameworkcode == 'FA' and CAN_user_editcatalogue_fast_cataloging ) %]
Lines 135-141 Link Here
135
157
136
[% IF ( CAN_user_editcatalogue_edit_catalogue || CAN_user_editcatalogue_edit_items ||
158
[% IF ( CAN_user_editcatalogue_edit_catalogue || CAN_user_editcatalogue_edit_items ||
137
CAN_user_serials_create_subscription ) %]
159
CAN_user_serials_create_subscription ) %]
138
    <div class="btn-group">
160
    <div id="newDropdownContainer" class="btn-group">
139
    <button class="btn btn-small dropdown-toggle" data-toggle="dropdown"><i class="icon-plus"></i> New <span class="caret"></span></button>
161
    <button class="btn btn-small dropdown-toggle" data-toggle="dropdown"><i class="icon-plus"></i> New <span class="caret"></span></button>
140
        <ul class="dropdown-menu">
162
        <ul class="dropdown-menu">
141
            [% IF ( CAN_user_editcatalogue_edit_catalogue ) %]
163
            [% IF ( CAN_user_editcatalogue_edit_catalogue ) %]
Lines 159-166 CAN_user_serials_create_subscription ) %] Link Here
159
    </div>
181
    </div>
160
[% END %]
182
[% END %]
161
183
162
[% IF ( CAN_user_editcatalogue_edit_catalogue || CAN_user_editcatalogue_edit_items || CAN_user_tools_items_batchmod || CAN_user_tools_items_batchdel ) or ( frameworkcode == 'FA' and CAN_user_editcatalogue_fast_cataloging ) %]
184
[% IF ( CAN_user_editcatalogue_edit_catalogue || CAN_user_editcatalogue_delete_catalogue || CAN_user_editcatalogue_edit_items || CAN_user_tools_items_batchmod || CAN_user_tools_items_batchdel ) or ( frameworkcode == 'FA' and CAN_user_editcatalogue_fast_cataloging ) %]
163
    <div class="btn-group">
185
    <div id="editDropdownContainer" class="btn-group">
164
    <button class="btn btn-small dropdown-toggle" data-toggle="dropdown"><i class="icon-pencil"></i> Edit <span class="caret"></span></button>
186
    <button class="btn btn-small dropdown-toggle" data-toggle="dropdown"><i class="icon-pencil"></i> Edit <span class="caret"></span></button>
165
        <ul class="dropdown-menu">
187
        <ul class="dropdown-menu">
166
            [% IF CAN_user_editcatalogue_edit_catalogue or ( frameworkcode == 'FA' and CAN_user_editcatalogue_fast_cataloging ) %]
188
            [% IF CAN_user_editcatalogue_edit_catalogue or ( frameworkcode == 'FA' and CAN_user_editcatalogue_fast_cataloging ) %]
Lines 197-203 CAN_user_serials_create_subscription ) %] Link Here
197
                <li><a href="#" id="z3950copy">Replace record via Z39.50/SRU</a></li>
219
                <li><a href="#" id="z3950copy">Replace record via Z39.50/SRU</a></li>
198
            [% END %]
220
            [% END %]
199
221
200
            [% IF CAN_user_editcatalogue_edit_catalogue or ( frameworkcode == 'FA' and CAN_user_editcatalogue_fast_cataloging ) %]
222
            [% IF CAN_user_editcatalogue_edit_catalogue or CAN_user_editcatalogue_delete_catalogue or ( frameworkcode == 'FA' and CAN_user_editcatalogue_fast_cataloging ) %]
201
                [% IF ( count ) %]
223
                [% IF ( count ) %]
202
                    <li class="disabled"><a id="deletebiblio" data-toggle="tooltip" data-placement="left" title="[% count %] item(s) are attached to this record. You must delete all items before deleting this record." href="#">Delete record</a></li>
224
                    <li class="disabled"><a id="deletebiblio" data-toggle="tooltip" data-placement="left" title="[% count %] item(s) are attached to this record. You must delete all items before deleting this record." href="#">Delete record</a></li>
203
                [% ELSE %]
225
                [% ELSE %]
Lines 217-223 CAN_user_serials_create_subscription ) %] Link Here
217
    </div>
239
    </div>
218
[% END %]
240
[% END %]
219
241
220
    <div class="btn-group">
242
    <div id="saveDropdownContainer" class="btn-group">
221
    <button class="btn btn-small dropdown-toggle" data-toggle="dropdown"><i class="icon-download-alt"></i> Save <span class="caret"></span></button>
243
    <button class="btn btn-small dropdown-toggle" data-toggle="dropdown"><i class="icon-download-alt"></i> Save <span class="caret"></span></button>
222
    <ul class="dropdown-menu">
244
    <ul class="dropdown-menu">
223
        <li><a href="/cgi-bin/koha/catalogue/export.pl?format=bibtex&amp;op=export&amp;bib=[% biblionumber %]">BIBTEX</a></li>
245
        <li><a href="/cgi-bin/koha/catalogue/export.pl?format=bibtex&amp;op=export&amp;bib=[% biblionumber %]">BIBTEX</a></li>
Lines 232-238 CAN_user_serials_create_subscription ) %] Link Here
232
    </div>
254
    </div>
233
255
234
[% IF ( virtualshelves && intranetbookbag ) %]
256
[% IF ( virtualshelves && intranetbookbag ) %]
235
    <div class="btn-group">
257
    <div id="addtoDropdownContainer" class="btn-group">
236
    <button class="btn btn-small dropdown-toggle" data-toggle="dropdown">Add to <span class="caret"></span></button>
258
    <button class="btn btn-small dropdown-toggle" data-toggle="dropdown">Add to <span class="caret"></span></button>
237
    <ul class="dropdown-menu">
259
    <ul class="dropdown-menu">
238
        <li><a href="#" id="addtocart">Cart</a></li>
260
        <li><a href="#" id="addtocart">Cart</a></li>
Lines 250-256 CAN_user_serials_create_subscription ) %] Link Here
250
[% IF ( CAN_user_reserveforothers ) %]
272
[% IF ( CAN_user_reserveforothers ) %]
251
    [% UNLESS ( norequests ) %]
273
    [% UNLESS ( norequests ) %]
252
        [% IF ( holdfor ) %]
274
        [% IF ( holdfor ) %]
253
            <div class="btn-group">
275
            <div id="placeholdDropdownContainer" class="btn-group">
254
                <button class="btn btn-small dropdown-toggle" data-toggle="dropdown">
276
                <button class="btn btn-small dropdown-toggle" data-toggle="dropdown">
255
                    <i class="icon-hold"></i>
277
                    <i class="icon-hold"></i>
256
                    Place hold
278
                    Place hold
(-)a/t/db_dependent/Api/V1/Biblios.pm (+96 lines)
Line 0 Link Here
1
package t::db_dependent::Api::V1::Biblios;
2
3
use Modern::Perl;
4
use Test::More;
5
6
use t::lib::TestObjects::BiblioFactory;
7
use t::lib::TestObjects::ItemFactory;
8
use t::lib::TestObjects::ObjectFactory;
9
10
sub delete_n_204 {
11
    my ($class, $restTest, $driver) = @_;
12
    my $testContext = $restTest->get_testContext(); #Test context will be automatically cleaned after this subtest has been executed.
13
    my $activeUser = $restTest->get_activeBorrower();
14
15
    #Create the test context.
16
    my $biblios = t::lib::TestObjects::BiblioFactory->createTestGroup(
17
                        {'biblio.title' => 'The significant chore of building test faculties',
18
                         'biblio.author'   => 'Programmer, Broken',
19
                         'biblio.copyrightdate' => '2015',
20
                         'biblioitems.isbn'     => '951967151337',
21
                         'biblioitems.itemtype' => 'BK',
22
                        }, undef, $testContext);
23
    my $biblionumber = $biblios->{'951967151337'}->{biblionumber};
24
25
    #Execute request
26
    my $path = $restTest->get_routePath();
27
    $path =~ s/\{biblionumber\}/$biblionumber/;
28
    $driver->delete_ok($path => {Accept => 'text/json'});
29
    $driver->status_is(204);
30
31
    #Confirm result
32
    my $record = C4::Biblio::GetBiblio( $biblios->{'951967151337'}->{biblionumber} );
33
    ok(not($record), "Biblio deletion confirmed");
34
35
    return 1;
36
}
37
38
sub delete_n_404 {
39
    my ($class, $restTest, $driver) = @_;
40
    my $testContext = $restTest->get_testContext(); #Test context will be automatically cleaned after this subtest has been executed.
41
    my $activeUser = $restTest->get_activeBorrower();
42
43
    #Create the test context.
44
    my $biblios = t::lib::TestObjects::BiblioFactory->createTestGroup(
45
                        {'biblio.title' => 'The significant chore of building test faculties',
46
                         'biblio.author'   => 'Programmer, Broken',
47
                         'biblio.copyrightdate' => '2015',
48
                         'biblioitems.isbn'     => '951967151337',
49
                         'biblioitems.itemtype' => 'BK',
50
                        }, undef, $testContext);
51
    my $biblionumber = $biblios->{'951967151337'}->{biblionumber};
52
    C4::Biblio::DelBiblio($biblionumber);
53
    #Now we have a biblionumber which certainly doesn't exists!
54
55
    #Execute request
56
    my $path = $restTest->get_routePath();
57
    $path =~ s/\{biblionumber\}/$biblionumber/;
58
    $driver->delete_ok($path => {Accept => 'text/json'});
59
    $driver->status_is(404);
60
61
    return 1;
62
}
63
64
sub delete_n_400 {
65
    my ($class, $restTest, $driver) = @_;
66
    my $testContext = $restTest->get_testContext(); #Test context will be automatically cleaned after this subtest has been executed.
67
    my $activeUser = $restTest->get_activeBorrower();
68
69
    #Create the test context.
70
    my $biblios = t::lib::TestObjects::BiblioFactory->createTestGroup(
71
                        {'biblio.title' => 'The significant chore of building test faculties',
72
                         'biblio.author'   => 'Programmer, Broken',
73
                         'biblio.copyrightdate' => '2015',
74
                         'biblioitems.isbn'     => '951967151337',
75
                         'biblioitems.itemtype' => 'BK',
76
                        }, undef, $testContext);
77
    my $biblionumber = $biblios->{'951967151337'}->{biblionumber};
78
    my $items = t::lib::TestObjects::ItemFactory->createTestGroup([
79
                                {biblionumber => $biblionumber,
80
                                 barcode => '11N01'}
81
                                ], undef, $testContext);
82
83
    #Execute request
84
    my $path = $restTest->get_routePath();
85
    $path =~ s/\{biblionumber\}/$biblionumber/;
86
    $driver->delete_ok($path => {Accept => 'text/json'});
87
    $driver->status_is(400);
88
89
    #Confirm result
90
    my $record = C4::Biblio::GetBiblio( $biblios->{'951967151337'}->{biblionumber} );
91
    ok($record, "Biblio deletion aborted due to attached Items");
92
93
    return 1;
94
}
95
96
1;
(-)a/t/db_dependent/Catalogue/detail.t (-1 / +89 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# Copyright 2015 Open Source Freedom Fighters
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use Test::More;
23
use Scalar::Util qw(blessed);
24
25
use t::lib::Page::Catalogue::Detail;
26
use t::lib::TestObjects::BorrowerFactory;
27
use t::lib::TestObjects::BiblioFactory;
28
use Koha::Auth::PermissionManager;
29
30
31
##Setting up the test context
32
my $testContext = {};
33
34
my $password = '1234';
35
my $borrowerFactory = t::lib::TestObjects::BorrowerFactory->new();
36
my $borrowers = $borrowerFactory->createTestGroup([
37
            {firstname  => 'Polli-Pantti',
38
             surname    => 'Pipi',
39
             cardnumber => '1A01',
40
             branchcode => 'CPL',
41
             userid     => 'pipi_padmin',
42
             password   => $password,
43
            },
44
        ], undef, $testContext);
45
46
##Test context set, starting testing:
47
eval { #run in a eval-block so we don't die without tearing down the test context
48
49
    subtest "Test Delete Biblio" => sub {
50
        testDeleteBiblio();
51
    };
52
53
};
54
if ($@) { #Catch all leaking errors and gracefully terminate.
55
    warn $@;
56
    tearDown();
57
    exit 1;
58
}
59
60
##All tests done, tear down test context
61
tearDown();
62
done_testing;
63
64
sub tearDown {
65
    t::lib::TestObjects::ObjectFactory->tearDownTestContext($testContext);
66
}
67
68
sub testDeleteBiblio {
69
    my $permissionManager = Koha::Auth::PermissionManager->new();
70
    $permissionManager->grantPermissions($borrowers->{'1A01'}, {catalogue => 'staff_login',
71
                                                                editcatalogue => 'delete_catalogue',
72
                                                              });
73
    my $biblios = t::lib::TestObjects::BiblioFactory->createTestGroup(
74
                        {'biblio.title' => 'The significant chore of building test faculties',
75
                         'biblio.author'   => 'Programmer, Broken',
76
                         'biblio.copyrightdate' => '2015',
77
                         'biblioitems.isbn'     => '951967151337',
78
                         'biblioitems.itemtype' => 'BK',
79
                        }, undef, $testContext);
80
81
    my $detail = t::lib::Page::Catalogue::Detail->new({biblionumber => $biblios->{'951967151337'}->{biblionumber}});
82
83
    $detail->doPasswordLogin($borrowers->{'1A01'}->userid, $password)
84
                ->isBiblioMatch($biblios->{'951967151337'})
85
                ->deleteBiblio();
86
87
    my $record = C4::Biblio::GetBiblio( $biblios->{'951967151337'}->{biblionumber} );
88
    ok(not($record), "Biblio deletion confirmed");
89
}

Return to bug 14656