From 8b3aad217b6869bff3d94aca98b7d54ace8ae6ec Mon Sep 17 00:00:00 2001
From: Alex Arnaud <alex.arnaud@biblibre.com>
Date: Fri, 8 Jul 2016 15:53:50 +0200
Subject: [PATCH] Bug 16881 - Apply KohaTable and ColVis plugin on MARCdetail's
 items table

Also, the columns displayed are saved for each users in database.

Test plan:

- apply this BZ,
- launch perl installer/data/mysql/updatedatabase.pl
- launch perl misc/devel/update_dbix_class_files.pl
- go to the items tab on cgi-bin/koha/catalogue/MARCdetail.pl page
  you should be able to hide/show each columns.
- leave this page and come back with the same koha account:
  You should have the same columns shown/hidden as when you left.
---
 C4/Utils/DataTables/ColumnsSettings.pm             | 101 ++++++++++----
 C4/Utils/DataTables/ColumnsSettings/Dynamic.pm     |  74 +++++++++++
 Koha/REST/V1/UserColumnsSettings.pm                |  62 +++++++++
 Koha/Template/Plugin/ColumnsSettings.pm            |   5 +-
 Koha/UsersColumnsSetting.pm                        |  50 +++++++
 Koha/UsersColumnsSettings.pm                       |  54 ++++++++
 api/v1/swagger/definitions.json                    |   6 +
 api/v1/swagger/definitions/column.json             |  13 ++
 .../swagger/definitions/user_columns_settings.json |  31 +++++
 api/v1/swagger/paths.json                          |   3 +
 api/v1/swagger/paths/user_columns_settings.json    |  46 +++++++
 catalogue/MARCdetail.pl                            |   5 +-
 .../add-table-users_columns_settings.sql           |  13 ++
 installer/data/mysql/kohastructure.sql             |  18 +++
 .../prog/en/includes/columns_settings.inc          |  33 ++++-
 .../prog/en/modules/catalogue/MARCdetail.tt        |  40 +++++-
 t/db_dependent/ColumnsSettings.t                   |   1 +
 t/db_dependent/api/v1/user_columns_settings.t      | 148 +++++++++++++++++++++
 18 files changed, 664 insertions(+), 39 deletions(-)
 create mode 100644 C4/Utils/DataTables/ColumnsSettings/Dynamic.pm
 create mode 100644 Koha/REST/V1/UserColumnsSettings.pm
 create mode 100644 Koha/UsersColumnsSetting.pm
 create mode 100644 Koha/UsersColumnsSettings.pm
 create mode 100644 api/v1/swagger/definitions/column.json
 create mode 100644 api/v1/swagger/definitions/user_columns_settings.json
 create mode 100644 api/v1/swagger/paths/user_columns_settings.json
 create mode 100644 installer/data/mysql/atomicupdate/add-table-users_columns_settings.sql
 create mode 100644 t/db_dependent/api/v1/user_columns_settings.t

diff --git a/C4/Utils/DataTables/ColumnsSettings.pm b/C4/Utils/DataTables/ColumnsSettings.pm
index 8713489..58ab8ae 100644
--- a/C4/Utils/DataTables/ColumnsSettings.pm
+++ b/C4/Utils/DataTables/ColumnsSettings.pm
@@ -6,8 +6,13 @@ use YAML;
 use C4::Context;
 use Koha::Database;
 use Koha::Caches;
+use Koha::UsersColumnsSettings;
+use Koha::UsersColumnsSetting;
+use C4::Utils::DataTables::ColumnsSettings::Dynamic;
 
 sub get_yaml {
+    my ( $module, $page, $tablename ) = @_;
+
     my $yml_path = C4::Context->config('intranetdir') . '/admin/columns_settings.yml';
     my $cache = Koha::Caches->get_instance();
     my $yaml  = $cache->get_from_cache('ColumnsSettingsYaml');
@@ -19,42 +24,63 @@ sub get_yaml {
         $cache->set_in_cache( 'ColumnsSettingsYaml', $yaml, { expiry => 3600 } );
     }
 
-    return $yaml;
+    unless ( $module || $page || $tablename ) {
+        return $yaml;
+    }
+
+    if (my $list = $yaml->{modules}{$module}{$page}{$tablename}) {
+        return $list;
+    }
+
+    return '';
 }
 
 sub get_columns {
-    my ( $module, $page, $tablename ) = @_;
-
-    my $list = get_yaml;
+    my ( $module, $page, $tablename, $additional_param ) = @_;
+
+    my $columns = get_yaml($module, $page, $tablename);
+    # Fall back on a dynamic way to retrieve default columns.
+    unless ($columns) {
+        my $sub = $module . '_' . $page . '_' . $tablename;
+        if (my $s = C4::Utils::DataTables::ColumnsSettings::Dynamic->can($sub)) {
+            $columns = $s->($additional_param);
+        }
+    }
 
     my $schema = Koha::Database->new->schema;
-
-    my $rs = $schema->resultset('ColumnsSetting')->search(
-        {
-            module    => $module,
-            page      => $page,
-            tablename => $tablename,
+    my $borrowernumber = C4::Context::userenv->{number};
+
+    foreach my $c (@$columns) {
+        # User settings.
+        my $user_column = Koha::UsersColumnsSettings->search({
+            borrowernumber   => $borrowernumber,
+            module           => $module,
+            page             => $page,
+            tablename        => $tablename,
+            columnname       => $c->{columnname},
+            additional_param => $additional_param
+        })->next();
+
+        if ($user_column) {
+            $c->{is_hidden} = $user_column->is_hidden || 0;
         }
-    );
-
-    while ( my $c = $rs->next ) {
-        my $column = first { $c->columnname eq $_->{columnname} }
-        @{ $list->{modules}{ $c->module }{ $c->page }{ $c->tablename } };
-        $column->{is_hidden}         = $c->is_hidden;
-        $column->{cannot_be_toggled} = $c->cannot_be_toggled;
-    }
 
-    my $columns = $list->{modules}{$module}{$page}{$tablename} || [];
+        # Admin settings.
+        my $column = $schema->resultset('ColumnsSetting')->search({
+            module     => $module,
+            page       => $page,
+            tablename  => $tablename,
+            columnname => $c->{columnname},
+        })->next();
 
-    # Assign default value if does not exist
-    $columns = [ map {
-        {
-            cannot_be_toggled => exists $_->{cannot_be_toggled} ? $_->{cannot_be_toggled} : 0,
-            cannot_be_modified => exists $_->{cannot_be_modified} ? $_->{cannot_be_modified} : 0,
-            is_hidden => exists $_->{is_hidden} ? $_->{is_hidden} : 0,
-            columnname => $_->{columnname},
+        if ($column) {
+            $c->{cannot_be_toggled} = $column->cannot_be_toggled;
         }
-    } @$columns ];
+
+        $c->{is_hidden} ||= 0;
+        $c->{cannot_be_toggled} ||= 0;
+        $c->{cannot_be_modified} ||= 0;
+    }
 
     return $columns;
 }
@@ -100,4 +126,25 @@ sub update_columns {
     }
 }
 
+sub update_user_columns {
+    my ($params) = @_;
+    my $columns = $params->{columns};
+
+    my $schema = Koha::Database->new->schema;
+
+    foreach my $colname (keys %$columns) {
+        $schema->resultset('UsersColumnsSetting')->update_or_create(
+            {
+                borrowernumber   => $params->{borrowernumber},
+                module           => $params->{module},
+                page             => $params->{page},
+                tablename        => $params->{tablename},
+                additional_param => $params->{additional_param},
+                columnname       => $colname,
+                is_hidden        => $columns->{$colname},
+            }
+        );
+    }
+}
+
 1;
diff --git a/C4/Utils/DataTables/ColumnsSettings/Dynamic.pm b/C4/Utils/DataTables/ColumnsSettings/Dynamic.pm
new file mode 100644
index 0000000..cfda84f
--- /dev/null
+++ b/C4/Utils/DataTables/ColumnsSettings/Dynamic.pm
@@ -0,0 +1,74 @@
+package C4::Utils::DataTables::ColumnsSettings::Dynamic;
+
+# Copyright 2017 BibLibre
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# Koha is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
+
+use Modern::Perl;
+require Exporter;
+
+use vars qw(@ISA @EXPORT);
+
+use C4::Biblio;
+use Koha::Database;
+
+BEGIN {
+
+    @ISA        = qw(Exporter);
+    @EXPORT     = qw(dt_build_orderby dt_build_having dt_get_params dt_build_query);
+}
+
+=head1 NAME
+
+C4::Utils::DataTables::ColumnsSettings::Dynamic - Subs providing dynamic columns for jquery datatables.
+
+=head1 SYNOPSIS
+
+use C4::Utils::DataTables::ColumnsSettings::Dynamic;
+
+=head1 DESCRIPTION
+
+This modules is intented to contain subs that return columns for KohaTable with columns setting plugin.
+
+=head1 FUNCTIONS
+
+=head2 catalogue_marcdetail_itemst
+
+    my $columns = catalogue_marcdetail_itemst($frameworkcode);
+    This function returns items columns depending on the framework.
+
+=cut
+
+sub catalogue_marcdetail_itemst {
+    my ($frameworkcode) = @_;
+
+    my $columns;
+    my $tagslib = C4::Biblio::GetMarcStructure( 1, $frameworkcode );
+    my ($itemfield) = C4::Biblio::GetMarcFromKohaField( 'items.itemnumber', $frameworkcode );
+    foreach my $subfield ( keys %{ $tagslib->{$itemfield} } ) {
+        next unless $subfield =~ /^\w$/;
+        next if ( $tagslib->{$itemfield}->{$subfield}->{tab} ne 10 );
+        next if ( $tagslib->{$itemfield}->{$subfield}->{hidden} =~ /-7|-4|-3|-2|2|3|5|8/ );
+
+        push @$columns, {
+            columnname         => $subfield,
+            is_hidden          => 0,
+        }
+    }
+    return $columns;
+}
+
+1;
diff --git a/Koha/REST/V1/UserColumnsSettings.pm b/Koha/REST/V1/UserColumnsSettings.pm
new file mode 100644
index 0000000..f1d0365
--- /dev/null
+++ b/Koha/REST/V1/UserColumnsSettings.pm
@@ -0,0 +1,62 @@
+package Koha::REST::V1::UserColumnsSettings;
+
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+
+use Mojo::Base 'Mojolicious::Controller';
+
+use C4::Auth qw( haspermission );
+use Koha::UsersColumnsSetting;
+use Koha::UsersColumnsSettings;
+
+use Try::Tiny;
+
+sub add {
+    my ( $c, $args, $cb ) = @_;
+
+    my $added;
+    my $body = $args->{body};
+    foreach my $column (@{ $body->{columns} }) {
+        my $params = {
+            borrowernumber => $body->{'borrowernumber'},
+            module => $body->{module},
+            page => $body->{page},
+            tablename => $body->{tablename},
+            additional_param => $body->{additional_param},
+            columnname => $column->{name},
+        };
+
+        my $user_column;
+        unless ( $user_column = Koha::UsersColumnsSettings->search($params)->next ) {
+            $user_column = Koha::UsersColumnsSetting->new($params);
+        }
+        $user_column->is_hidden($column->{is_hidden});
+
+        try {
+            $user_column->store;
+            push @$added, $user_column->unblessed;
+        }
+        catch {
+            warn $_;
+            return $c->$cb( $_, 500);
+        };
+    }
+
+    return $c->$cb( { added => $added }, 200);
+}
+
+1;
diff --git a/Koha/Template/Plugin/ColumnsSettings.pm b/Koha/Template/Plugin/ColumnsSettings.pm
index 4617969..7ef74fc 100644
--- a/Koha/Template/Plugin/ColumnsSettings.pm
+++ b/Koha/Template/Plugin/ColumnsSettings.pm
@@ -27,6 +27,7 @@ use JSON qw( to_json );
 
 use C4::Context qw( config );
 use C4::Utils::DataTables::ColumnsSettings;
+use C4::Utils::DataTables::ColumnsSettings::Dynamic;
 
 =pod
 
@@ -42,10 +43,10 @@ For example: [% ColumnsSettings.GetColumns( 'circ', 'circulation', 'holdst' ) %]
 =cut
 
 sub GetColumns {
-    my ( $self, $module, $page, $table, $format ) = @_;
+    my ( $self, $module, $page, $table, $format, $additional_params ) = @_;
     $format //= q{};
 
-    my $columns = C4::Utils::DataTables::ColumnsSettings::get_columns( $module, $page, $table );
+    my $columns = C4::Utils::DataTables::ColumnsSettings::get_columns( $module, $page, $table, $additional_params );
 
     return $format eq 'json'
         ? to_json( $columns )
diff --git a/Koha/UsersColumnsSetting.pm b/Koha/UsersColumnsSetting.pm
new file mode 100644
index 0000000..55111a5
--- /dev/null
+++ b/Koha/UsersColumnsSetting.pm
@@ -0,0 +1,50 @@
+package Koha::UsersColumnsSetting;
+
+# Copyright Biblibre 2017
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+
+use Koha::Database;
+
+use base qw(Koha::Object);
+
+=head1 NAME
+
+Koha::UsersColumnsSetting - Koha UsersColumnsSetting Object class
+
+=head1 API
+
+=head2 Class Methods
+
+=cut
+
+=head3 type
+
+=cut
+
+sub _type {
+    return 'UsersColumnsSetting';
+}
+
+=head1 AUTHOR
+
+Alex Arnaud <alex.arnaud@biblibre.com>
+
+=cut
+
+1;
diff --git a/Koha/UsersColumnsSettings.pm b/Koha/UsersColumnsSettings.pm
new file mode 100644
index 0000000..cab57d4
--- /dev/null
+++ b/Koha/UsersColumnsSettings.pm
@@ -0,0 +1,54 @@
+package Koha::UsersColumnsSettings;
+
+# Copyright Biblibre 2017
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+
+use Koha::Database;
+
+use base qw(Koha::Objects);
+
+=head1 NAME
+
+Koha::UsersColumnsSettings - Koha UsersColumnsSettings Object class
+
+=head1 API
+
+=head2 Class Methods
+
+=cut
+
+=head3 type
+
+=cut
+
+sub _type {
+    return 'UsersColumnsSetting';
+}
+
+sub object_class {
+    return 'Koha::UsersColumnsSetting';
+}
+
+=head1 AUTHOR
+
+Alex Arnaud <alex.arnaud@biblibre.com>
+
+=cut
+
+1;
\ No newline at end of file
diff --git a/api/v1/swagger/definitions.json b/api/v1/swagger/definitions.json
index d27167b..045fc84 100644
--- a/api/v1/swagger/definitions.json
+++ b/api/v1/swagger/definitions.json
@@ -13,5 +13,11 @@
   },
   "error": {
     "$ref": "definitions/error.json"
+  },
+  "user_columns_settings": {
+    "$ref": "definitions/user_columns_settings.json"
+  },
+  "column": {
+    "$ref": "definitions/column.json"
   }
 }
diff --git a/api/v1/swagger/definitions/column.json b/api/v1/swagger/definitions/column.json
new file mode 100644
index 0000000..1d66489
--- /dev/null
+++ b/api/v1/swagger/definitions/column.json
@@ -0,0 +1,13 @@
+{
+    "type": "object",
+    "properties": {
+        "name": {
+            "description": "Column name",
+            "type": "string"
+        },
+        "is_hidden": {
+            "description": "Column is hidden or not",
+            "type": "boolean"
+        }
+    }
+}
diff --git a/api/v1/swagger/definitions/user_columns_settings.json b/api/v1/swagger/definitions/user_columns_settings.json
new file mode 100644
index 0000000..2a12629
--- /dev/null
+++ b/api/v1/swagger/definitions/user_columns_settings.json
@@ -0,0 +1,31 @@
+{
+  "type": "object",
+  "properties": {
+    "borrowernumber": {
+      "$ref": "../x-primitives.json#/borrowernumber"
+    },
+    "module": {
+      "description": "Module name",
+      "type": "string"
+    },
+    "page": {
+      "description": "Page name",
+      "type": "string"
+    },
+    "tablename": {
+      "description": "Table name",
+      "type": "string"
+    },
+    "additional_param": {
+      "description": "Additional parameter",
+      "type": "string"
+    },
+    "columns": {
+      "description": "Columns list",
+      "type": "array",
+      "items": {
+        "$ref": "column.json"
+      }
+    }
+  }
+}
diff --git a/api/v1/swagger/paths.json b/api/v1/swagger/paths.json
index b1fcf40..dc37fe7 100644
--- a/api/v1/swagger/paths.json
+++ b/api/v1/swagger/paths.json
@@ -16,5 +16,8 @@
   },
   "/patrons/{borrowernumber}": {
     "$ref": "paths/patrons.json#/~1patrons~1{borrowernumber}"
+  },
+  "/user_columns_settings": {
+    "$ref": "paths/user_columns_settings.json#/~1user_columns_settings"
   }
 }
diff --git a/api/v1/swagger/paths/user_columns_settings.json b/api/v1/swagger/paths/user_columns_settings.json
new file mode 100644
index 0000000..c1061f5
--- /dev/null
+++ b/api/v1/swagger/paths/user_columns_settings.json
@@ -0,0 +1,46 @@
+{
+  "/user_columns_settings": {
+    "post": {
+      "x-mojo-controller": "Koha::REST::V1::UserColumnsSettings",
+      "operationId": "add",
+      "tags": ["user_columns_settings"],
+      "parameters": [{
+        "name": "body",
+        "in": "body",
+        "description": "A JSON object containing informations about the columns settings",
+        "required": true,
+        "schema": {
+          "$ref": "../definitions.json#/user_columns_settings"
+        }
+      }],
+      "produces": [
+        "application/json"
+      ],
+      "responses": {
+        "200": {
+          "description": "Columns settings added",
+          "schema": {
+            "$ref": "../definitions.json#/user_columns_settings"
+          }
+        },
+        "403": {
+          "description": "Access forbidden",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        },
+        "500": {
+          "description": "Internal error",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        }
+      },
+      "x-koha-authorization": {
+        "permissions": {
+          "catalogue": "1"
+        }
+      }
+    }
+  }
+}
diff --git a/catalogue/MARCdetail.pl b/catalogue/MARCdetail.pl
index 09f9778..52732f1 100755
--- a/catalogue/MARCdetail.pl
+++ b/catalogue/MARCdetail.pl
@@ -85,6 +85,9 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
         debug           => 1,
     }
 );
+$template->param(
+    frameworkcode   => $frameworkcode
+);
 
 my $record = GetMarcBiblio($biblionumber, 1);
 $template->param( ocoins => GetCOinSBiblio($record) );
@@ -281,7 +284,7 @@ my ($holdingbrtagf,$holdingbrtagsubf) = &GetMarcFromKohaField("items.holdingbran
 # fill item info
 my @item_header_loop;
 for my $subfield_code ( @item_subfield_codes ) {
-    push @item_header_loop, $witness{$subfield_code};
+    push @item_header_loop, { colname => $subfield_code, label => $witness{$subfield_code} };
     for my $item_data ( @item_loop ) {
         $item_data->{$subfield_code} ||= "&nbsp;"
     }
diff --git a/installer/data/mysql/atomicupdate/add-table-users_columns_settings.sql b/installer/data/mysql/atomicupdate/add-table-users_columns_settings.sql
new file mode 100644
index 0000000..8388632
--- /dev/null
+++ b/installer/data/mysql/atomicupdate/add-table-users_columns_settings.sql
@@ -0,0 +1,13 @@
+CREATE TABLE IF NOT EXISTS users_columns_settings (
+    id int(11) NOT NULL auto_increment,
+    borrowernumber int(11) NOT NULL,
+    module varchar(50) NOT NULL,
+    page varchar(50) NOT NULL,
+    tablename varchar(50) NOT NULL,
+    additional_param varchar(255) NOT NULL,
+    columnname varchar(50) NOT NULL,
+    is_hidden int(1) NOT NULL DEFAULT 0,
+    PRIMARY KEY(id),
+    UNIQUE KEY user_column_elements (borrowernumber, module, page, tablename, columnname, additional_param),
+    CONSTRAINT borrower_columns_settings_ibfk1 FOREIGN KEY (borrowernumber) REFERENCES borrowers (borrowernumber) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
\ No newline at end of file
diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql
index 2533e9d..2d3ba42 100644
--- a/installer/data/mysql/kohastructure.sql
+++ b/installer/data/mysql/kohastructure.sql
@@ -3592,6 +3592,24 @@ CREATE TABLE IF NOT EXISTS columns_settings (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
 
 --
+-- Table structure for table `users_columns_settings`
+--
+
+CREATE TABLE IF NOT EXISTS users_columns_settings (
+    id int(11) NOT NULL auto_increment,
+    borrowernumber int(11) NOT NULL,
+    module varchar(50) NOT NULL,
+    page varchar(50) NOT NULL,
+    tablename varchar(50) NOT NULL,
+    additional_param varchar(255) NOT NULL,
+    columnname varchar(50) NOT NULL,
+    is_hidden int(1) NOT NULL DEFAULT 0,
+    PRIMARY KEY(id),
+    UNIQUE KEY user_column_elements (borrowernumber, module, page, tablename, columnname, additional_param),
+    CONSTRAINT borrower_columns_settings_ibfk1 FOREIGN KEY (borrowernumber) REFERENCES borrowers (borrowernumber) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+--
 -- Table structure for table 'items_search_fields'
 --
 
diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/columns_settings.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/columns_settings.inc
index b065a35..a0a99d3 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/includes/columns_settings.inc
+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/columns_settings.inc
@@ -1,12 +1,13 @@
 [% USE ColumnsSettings %]
 
 <script type="text/javascript">
-function KohaTable(selector, dt_parameters, columns_settings) {
+var loaded = false;
+function KohaTable(selector, dt_parameters, columns_settings, script_params) {
     var id = 0;
     var hidden_ids = [];
     var included_ids = [];
     $(columns_settings).each( function() {
-        var named_id = $( 'thead th[data-colname="' + this.columnname + '"]', selector ).index( 'th' );
+        var named_id = $( 'thead th[data-colname="' + this.columnname + '"]', selector ).index();
         var used_id = dt_parameters.bKohaColumnsUseNames ? named_id : id;
         if ( used_id == -1 ) return;
 
@@ -25,13 +26,41 @@ function KohaTable(selector, dt_parameters, columns_settings) {
             text: _("Column visibility"),
         }
     ];
+    if ($(columns_settings).length) {
+        dt_parameters[ "stateSave" ] =  true;
+        dt_parameters[ "stateSaveCallback" ] = function(settings, data) {
+            KohaStateSaveCallback(settings, data, script_params);
+        };
+    }
     var table = $(selector).dataTable($.extend(true, {}, dataTablesDefaults, dt_parameters));
 
     $(hidden_ids).each(function(index, value) {
         table.fnSetColumnVis( value, false );
     });
 
+    loaded = true;
     return table;
 }
 
+function KohaStateSaveCallback(settings, data, script_params) {
+    if (loaded && typeof settings['aoColumns'] !== 'undefined') {
+        var columns = [];
+        $.each( settings['aoColumns'], function( index, value ){
+            columns.push({name: value.colname, is_hidden: value.bVisible ? false : true});
+        });
+        $.ajax({
+            url: "/api/v1/user_columns_settings",
+            data: JSON.stringify({
+                borrowernumber: "[% loggedinusernumber %]",
+                module: script_params['module'],
+                page: script_params['page'],
+                tablename: script_params['tablename'],
+                additional_param: script_params['additional_param'],
+                columns: columns
+            }),
+            type: "POST",
+        });
+    }
+}
+
 </script>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/MARCdetail.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/MARCdetail.tt
index 004d77f..56826be 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/MARCdetail.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/MARCdetail.tt
@@ -1,3 +1,4 @@
+[% USE ColumnsSettings %]
 [% INCLUDE 'doc-head-open.inc' %]
 <title>Koha &rsaquo; Catalog &rsaquo;
   [% IF ( unknownbiblionumber ) %]
@@ -6,8 +7,11 @@
     MARC details for [% bibliotitle | html %]
   [% END %]
 </title>
+<link rel="stylesheet" type="text/css" href="[% interface %]/[% theme %]/css/datatables.css" />
 [% INCLUDE 'doc-head-close.inc' %]
 [% INCLUDE 'browser-strings.inc' %]
+[% INCLUDE 'datatables.inc' %]
+[% INCLUDE 'columns_settings.inc' %]
 <!--[if lt IE 9]>
 <script type="text/javascript" src="[% interface %]/lib/shims/json2.min.js"></script>
 <![endif]-->
@@ -17,12 +21,30 @@
     var browser = KOHA.browser('[% searchid %]', parseInt('[% biblionumber %]', 10));
     browser.show();
 
-	 $(document).ready(function() {
-        $('#bibliotabs').tabs();
-        $("#Frameworks").on("change",function(){
-            Changefwk(this);
-        });
-	 });
+$(document).ready(function() {
+    $('#bibliotabs').tabs();
+    $("#Frameworks").on("change",function(){
+        Changefwk(this);
+    });
+
+    var columns_settings = [% ColumnsSettings.GetColumns( 'catalogue', 'marcdetail', 'itemst', 'json', frameworkcode ) %];
+    var script_params = {
+        module: 'catalogue',
+        page: 'marcdetail',
+        tablename: 'itemst',
+        additional_param: '[% frameworkcode %]',
+    }
+    var itemst = KohaTable("#tab10XX table", {
+        'module': 'catalogue',
+        'bPaginate': false,
+        'bInfo': false,
+        "bAutoWidth": false,
+        "bKohaColumnsUseNames": true,
+        "aoColumnDefs": [
+            { "bSortable": false, "bSearchable": false, "aTargets": [ "NoSort" ] }
+        ]
+    }, columns_settings, script_params);
+});
 
 function Changefwk(FwkList) {
 	var fwk = FwkList.options[FwkList.selectedIndex].value;
@@ -173,11 +195,14 @@ function Changefwk(FwkList) {
      [% IF ( tab10XX ) %]
     <div id="tab10XX">
         <table>
+            <thead>
                 <tr>
                     [% FOREACH header IN item_header_loop %]
-                        <th>[% header %]</th>
+                        <th data-colname="[% header.colname %]">[% header.label %]</th>
                     [% END %]
                 </tr>
+            </thead>
+            <tbody>
                 [% FOREACH item IN item_loop %]
                     <tr>
                         [% FOREACH sf_code IN item_subfield_codes %]
@@ -185,6 +210,7 @@ function Changefwk(FwkList) {
                         [% END %]
                     </tr>
                 [% END %]
+            </tbody>
         </table>
     </div>
     [% END %]
diff --git a/t/db_dependent/ColumnsSettings.t b/t/db_dependent/ColumnsSettings.t
index da75931..1a7049c 100644
--- a/t/db_dependent/ColumnsSettings.t
+++ b/t/db_dependent/ColumnsSettings.t
@@ -6,6 +6,7 @@ use Test::MockModule;
 
 use C4::Context;
 use C4::Utils::DataTables::ColumnsSettings;
+
 my $dbh = C4::Context->dbh;
 $dbh->{AutoCommit} = 0;
 $dbh->{RaiseError} = 1;
diff --git a/t/db_dependent/api/v1/user_columns_settings.t b/t/db_dependent/api/v1/user_columns_settings.t
new file mode 100644
index 0000000..98e0753
--- /dev/null
+++ b/t/db_dependent/api/v1/user_columns_settings.t
@@ -0,0 +1,148 @@
+#!/usr/bin/env perl
+
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+
+use Test::More tests => 9;
+use Test::Mojo;
+
+use t::lib::TestBuilder;
+use t::lib::Mocks;
+
+use C4::Auth;
+use Koha::UsersColumnsSetting;
+use Koha::UsersColumnsSettings;
+use Koha::Database;
+
+my $schema  = Koha::Database->new->schema;
+$schema->storage->txn_begin;
+my $builder = t::lib::TestBuilder->new;
+
+t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' );
+
+my $remote_address = '127.0.0.1';
+my $t              = Test::Mojo->new('Koha::REST::V1');
+
+my ( $unauthorized_borrowernumber, $unauthorized_session_id ) =
+  create_user_and_session( { authorized => 0 } );
+my ( $authorized_borrowernumber, $authorized_session_id ) =
+  create_user_and_session( { authorized => 1 } );
+
+my $user_columns_settings = {
+    borrowernumber => $authorized_borrowernumber,
+    module => 'catalgue',
+    page => 'marcdetail',
+    tablename => 'itemst',
+    additional_param => 'ACQ',
+    columns => [
+        {name => 'foo', is_hidden => \0},
+        {name => 'bar', is_hidden => \1}
+    ]
+};
+
+my $tx = $t->ua->build_tx( POST => "/api/v1/user_columns_settings/" => json => $user_columns_settings );
+$tx->req->cookies(
+    { name => 'CGISESSID', value => $unauthorized_session_id } );
+$tx->req->env( { REMOTE_ADDR => $remote_address } );
+$t->request_ok($tx)->status_is(403);
+
+$tx = $t->ua->build_tx( POST => "/api/v1/user_columns_settings/" => json => $user_columns_settings );
+$tx->req->cookies(
+    { name => 'CGISESSID', value => $authorized_session_id } );
+$tx->req->env( { REMOTE_ADDR => $remote_address } );
+$t->request_ok($tx)->status_is(200);
+
+my $col_foo = Koha::UsersColumnsSettings->search({
+    borrowernumber => $authorized_borrowernumber,
+    module => 'catalgue',
+    page => 'marcdetail',
+    tablename => 'itemst',
+    additional_param => 'ACQ',
+    columnname => 'foo'
+
+})->next;
+
+is($col_foo->is_hidden, 0, 'Column foo added');
+
+my $col_bar = Koha::UsersColumnsSettings->search({
+    borrowernumber => $authorized_borrowernumber,
+    module => 'catalgue',
+    page => 'marcdetail',
+    tablename => 'itemst',
+    additional_param => 'ACQ',
+    columnname => 'bar'
+})->next;
+
+is($col_bar->is_hidden, 1, 'Column bar added');
+
+# Update user columns
+$user_columns_settings->{columns}->[0]->{is_hidden} = \1;
+
+$tx = $t->ua->build_tx( POST => "/api/v1/user_columns_settings/" => json => $user_columns_settings );
+$tx->req->cookies(
+    { name => 'CGISESSID', value => $authorized_session_id } );
+$tx->req->env( { REMOTE_ADDR => $remote_address } );
+$t->request_ok($tx)->status_is(200);
+
+$col_foo = Koha::UsersColumnsSettings->search({
+    borrowernumber => $authorized_borrowernumber,
+    module => 'catalgue',
+    page => 'marcdetail',
+    tablename => 'itemst',
+    additional_param => 'ACQ',
+    columnname => 'foo'
+
+})->next;
+
+is($col_foo->is_hidden, 1, 'Column foo updated');
+
+sub create_user_and_session {
+
+    my $args  = shift;
+    my $flags = ( $args->{authorized} ) ? $args->{authorized} : 0;
+    my $dbh   = C4::Context->dbh;
+
+    my $user = $builder->build(
+        {
+            source => 'Borrower',
+            value  => {
+                flags => $flags
+            }
+        }
+    );
+
+    # Create a session for the authorized user
+    my $session = C4::Auth::get_session('');
+    $session->param( 'number',   $user->{borrowernumber} );
+    $session->param( 'id',       $user->{userid} );
+    $session->param( 'ip',       '127.0.0.1' );
+    $session->param( 'lasttime', time() );
+    $session->flush;
+
+    if ( $args->{authorized} ) {
+        $dbh->do( "
+            INSERT INTO user_permissions (borrowernumber,module_bit,code)
+            VALUES (?,3,'parameters_remaining_permissions')", undef,
+            $user->{borrowernumber} );
+    }
+
+    return ( $user->{borrowernumber}, $session->id );
+}
+
+$schema->storage->txn_rollback;
+
+1;
\ No newline at end of file
-- 
2.7.4