From e69394f6cdac5dcda9046d922f18f7b90bdc042c Mon Sep 17 00:00:00 2001
From: Dorian Merz <merz@ub.uni-leipzig.de>
Date: Thu, 14 Nov 2019 10:30:04 +0100
Subject: [PATCH] refs #16050 [fid_bbi] user Tags * activates user list tags *
 translations for "Your Tags" * show all tags on favorites overview ** fixes
 bug inherited from bootstrap3 theme * show all tags for user in select list
 ** adapts VuFind-core DB query ** TODO: Is this a Bug? File a Pull Request

---
 fid_bbi/config/vufind/config.ini              |   5 +-
 fid_bbi/languages/de.ini                      |   6 +-
 fid_bbi/languages/en.ini                      |   6 +-
 module/fid_bbi/config/module.config.php       |  18 ++
 module/fid_bbi/src/fid_bbi/Db/Table/Tags.php  | 104 ++++++++
 .../DefaultRecord/list-entry.phtml            | 228 ++++++++++++++++++
 6 files changed, 364 insertions(+), 3 deletions(-)
 create mode 100644 module/fid_bbi/src/fid_bbi/Db/Table/Tags.php
 create mode 100644 themes/fid_bbi/templates/RecordDriver/DefaultRecord/list-entry.phtml

diff --git a/fid_bbi/config/vufind/config.ini b/fid_bbi/config/vufind/config.ini
index 7ccc05e0eb2..96774c7c958 100644
--- a/fid_bbi/config/vufind/config.ini
+++ b/fid_bbi/config/vufind/config.ini
@@ -118,4 +118,7 @@ sender_email    = "noreply@hab.de"
 [Mail]
 ; enable search tools (send, export, ...) without login
 require_login = false
-user_email_in_to = true
\ No newline at end of file
+user_email_in_to = true
+
+[Social]
+tags = enabled
\ No newline at end of file
diff --git a/fid_bbi/languages/de.ini b/fid_bbi/languages/de.ini
index 5784dacadb3..08599f5477d 100644
--- a/fid_bbi/languages/de.ini
+++ b/fid_bbi/languages/de.ini
@@ -438,4 +438,8 @@ report_errors_send_success = "Vielen Dank für Ihr Feedback."
 history_saved_searches  = "Meine gespeicherten Suchen"
 
 #15969
-copied_link_to_clipboard = "Link in Zwischenablage kopiert"
\ No newline at end of file
+copied_link_to_clipboard = "Link in Zwischenablage kopiert"
+
+#16050
+add_tag_note = "Trennen Sie Tags mit einem Leerzeichen oder mit [ENTER]. Max. Länge eines Tags: 25 Zeichen."
+Your Tags = Meine Tags
\ No newline at end of file
diff --git a/fid_bbi/languages/en.ini b/fid_bbi/languages/en.ini
index f64e548a518..136c18cbbc6 100644
--- a/fid_bbi/languages/en.ini
+++ b/fid_bbi/languages/en.ini
@@ -431,4 +431,8 @@ Your Profile = "My Profile"
 history_saved_searches  = "My Saved Searches"
 
 #15969
-copied_link_to_clipboard = "Copied Link to Clipboard"
\ No newline at end of file
+copied_link_to_clipboard = "Copied Link to Clipboard"
+
+#16050
+add_tag_note = "Use a whitespace character or press [ENTER] to separate tags. Maximum length of a tag is 25 characters."
+Your Tags = My Tags
\ No newline at end of file
diff --git a/module/fid_bbi/config/module.config.php b/module/fid_bbi/config/module.config.php
index 6f02bff9c41..f5197d56401 100644
--- a/module/fid_bbi/config/module.config.php
+++ b/module/fid_bbi/config/module.config.php
@@ -19,6 +19,11 @@
  * @license http://opensource.org/licenses/gpl-2.0.php GNU GPLv2
  */
 
+use VuFind\Db\Table\Tags as BaseTagsTable;
+use fid_bbi\Db\Table\Tags as BBITagsTable;
+use VuFind\Db\Row\Tags as BaseTagsRow;
+use VuFind\Db\Table\GatewayFactory as TableGatewayFactory;
+
 $config = [
     'forms'       => require_once 'form.config.php',
     'controllers' => [
@@ -64,6 +69,19 @@ $config = [
                     'worldcat'        => 'fid_bbi\RecordTab\Worldcat',
                 ],
             ],
+            'db_table'          => [
+                'aliases'    => [
+                    BaseTagsTable::class => BBITagsTable::class,
+                ],
+                'factories'  => [
+                    BBITagsTable::class => TableGatewayFactory::class,
+                ]
+            ],
+            'db_row'            => [
+                'aliases'    => [
+                    'fid_bbi\Db\Row\Tags' => BaseTagsRow::class,
+                ]
+            ]
         ],
         'recorddriver_tabs'            => [
             'finc\RecordDriver\SolrDefault'  => [
diff --git a/module/fid_bbi/src/fid_bbi/Db/Table/Tags.php b/module/fid_bbi/src/fid_bbi/Db/Table/Tags.php
new file mode 100644
index 00000000000..04199f1d938
--- /dev/null
+++ b/module/fid_bbi/src/fid_bbi/Db/Table/Tags.php
@@ -0,0 +1,104 @@
+<?php
+/**
+ * Table Definition for tags
+ *
+ * PHP version 7
+ *
+ * Copyright (C) Villanova University 2010.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  Db_Table
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org Main Site
+ */
+namespace fid_bbi\Db\Table;
+
+use VuFind\Db\Table\Tags as BaseTags;
+use Zend\Db\Sql\Expression;
+
+/**
+ * Table Definition for tags
+ *
+ * @category VuFind
+ * @package  Db_Table
+ * @author   Dorian Merz <merz@ub.uni-leipzig.de>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org Main Site
+ */
+class Tags extends BaseTags
+{
+    /**
+     * Get a list of all tags generated by the user in favorites lists.  Note that
+     * the returned list WILL NOT include tags attached to records that are not
+     * saved in favorites lists.
+     *
+     * @param string $userId     User ID to look up.
+     * @param string $resourceId Filter for tags tied to a specific resource (null
+     * for no filter).
+     * @param int    $listId     Filter for tags tied to a specific list (null for no
+     * filter).
+     * @param string $source     Filter for tags tied to a specific record source
+     * (null for no filter).
+     *
+     * @return \Zend\Db\ResultSet\AbstractResultSet
+     */
+    public function getForUser($userId, $resourceId = null, $listId = null,
+        $source = null
+    ) {
+        $callback = function ($select) use ($userId, $resourceId, $listId, $source) {
+            $select->columns(
+                [
+                    'id' => new Expression(
+                        'min(?)', ['tags.id'],
+                        [Expression::TYPE_IDENTIFIER]
+                    ),
+                    'tag' => $this->caseSensitive
+                        ? 'tag' : new Expression('lower(tag)'),
+                    'cnt' => new Expression(
+                        'COUNT(DISTINCT(?))', ['rt.resource_id'],
+                        [Expression::TYPE_IDENTIFIER]
+                    )
+                ]
+            );
+            $select->join(
+                ['rt' => 'resource_tags'], 'tags.id = rt.tag_id', []
+            );
+            $select->join(
+                ['r' => 'resource'], 'rt.resource_id = r.id', []
+            );
+            $select->join(
+                ['ur' => 'user_resource'], 'r.id = ur.resource_id', []
+            );
+            $select->group(['tag'])->order([new Expression('lower(tag)')]);
+
+            $select->where->equalTo('ur.user_id', $userId)
+                ->equalTo('rt.user_id', $userId);
+
+            if (null !== $source) {
+                $select->where->equalTo('r.source', $source);
+            }
+
+            if (null !== $resourceId) {
+                $select->where->equalTo('r.record_id', $resourceId);
+            }
+            if (null !== $listId) {
+                $select->where->equalTo('rt.list_id', $listId);
+            }
+        };
+        return $this->select($callback);
+    }
+}
diff --git a/themes/fid_bbi/templates/RecordDriver/DefaultRecord/list-entry.phtml b/themes/fid_bbi/templates/RecordDriver/DefaultRecord/list-entry.phtml
new file mode 100644
index 00000000000..44822387e0c
--- /dev/null
+++ b/themes/fid_bbi/templates/RecordDriver/DefaultRecord/list-entry.phtml
@@ -0,0 +1,228 @@
+<!-- fid_bbi: RecordDriver - DefaultRecord - list-entry -->
+<?php /*copied from finc */?>
+<?php
+// Set up some convenience variables:
+$id = $this->driver->getUniqueId();
+$source = $this->driver->getSourceIdentifier();
+if (isset($this->list) && is_object($this->list)) {
+  $list_id = $this->list->id;
+  $user_id = $this->list->user_id;
+} else {
+  $list_id = null;
+  $user_id = $this->user ? $this->user->id : null;
+}
+// finc: next line finc-specific; required to display public favorites lists, #12052, see also below - CK
+$isEditable = $this->user && $this->user->id === $user_id;
+// Thumbnail
+$coverDetails = $this->record($this->driver)->getCoverDetails('list-entry', 'medium', $this->recordLink()->getUrl($this->driver));
+$cover = $coverDetails['html'];
+$thumbnail = false;
+$thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('list');
+if ($cover):
+  ob_start(); ?>
+  <div class="media-<?=$thumbnailAlignment?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>">
+    <?=$cover?>
+  </div>
+  <?php $thumbnail = ob_get_contents(); ?>
+  <?php ob_end_clean(); ?>
+<?php endif; ?>
+<div class="result<?php if ($this->driver->supportsAjaxStatus()): ?> ajaxItem<?php endif ?>">
+  <input type="hidden" value="<?=$this->escapeHtmlAttr($id)?>" class="hiddenId"/>
+  <input type="hidden" value="<?=$this->escapeHtmlAttr($source)?>" class="hiddenSource"/>
+  <?=$this->record($this->driver)->getCheckbox()?>
+  <div class="media">
+    <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?>
+      <?=$thumbnail?>
+    <?php endif; ?>
+    <div class="media-body">
+      <div class="result-body">
+        <div class="resultItemLine1">
+          <?php $missing = $this->driver instanceof \VuFind\RecordDriver\Missing; ?>
+          <?php if (!$missing): ?><a href="<?=$this->recordLink()->getUrl($this->driver)?>" class="getFull" data-view="<?=$this->params->getOptions()->getListViewOption()?>"><?php endif; ?>
+            <span class="title"><?=$this->record($this->driver)->getTitleHtml()?></span>
+            <?php if (!$missing): ?></a><?php endif; ?>
+        </div>
+
+        <div class="resultItemLine2">
+          <?php if ($this->driver->isCollection()): ?>
+            <?=implode('<br>', array_map(array($this, 'escapeHtml'), $this->driver->getSummary()));?>
+          <?php else: ?>
+            <?php $summAuthors = $this->driver->getPrimaryAuthors();
+            if (!empty($summAuthors)): ?>
+              <?=$this->transEsc('by')?>
+              <?php $authorCount = count($summAuthors);
+              foreach ($summAuthors as $i => $summAuthor): ?>
+                <a href="<?=$this->record($this->driver)->getLink('author', $summAuthor)?>"><?=$this->escapeHtml($summAuthor)?></a><?=($i + 1 < $authorCount ? ';' : '')?>
+              <?php endforeach; ?>
+            <?php endif; ?>
+
+            <?php $journalTitle = $this->driver->getContainerTitle();
+            $summDate = $this->driver->getPublicationDates(); ?>
+            <?php if (!empty($journalTitle)): ?>
+              <?=!empty($summAuthor) ? '<br/>' : ''?>
+              <?=/* TODO: handle highlighting more elegantly here */
+              $this->transEsc('Published in') . ' <a href="' . $this->record($this->driver)->getLink('journaltitle', str_replace(array('{{{{START_HILITE}}}}', '{{{{END_HILITE}}}}'), '', $journalTitle)) . '">' . $this->highlight($journalTitle) . '</a>';?>
+              <?=!empty($summDate) ? ' (' . $this->escapeHtml($summDate[0]) . ')' : ''?>
+            <?php elseif (!empty($summDate)): ?>
+              <?=!empty($summAuthor) ? '<br/>' : ''?>
+              <?=$this->transEsc('Published') . ' ' . $this->escapeHtml($summDate[0])?>
+            <?php endif; ?>
+            <?php $summInCollection = $this->driver->getContainingCollections();
+            if (false && !empty($summInCollection)): ?>
+              <?php foreach ($summInCollection as $collId => $collText): ?>
+                <div>
+                  <b><?=$this->transEsc("in_collection_label")?></b>
+                  <a class="collectionLinkText" href="<?=$this->url('collection', array('id' => $collId))?>?recordID=<?=urlencode($this->driver->getUniqueID())?>">
+                    <?=$this->escapeHtml($collText)?>
+                  </a>
+                </div>
+              <?php endforeach; ?>
+            <?php endif; ?>
+          <?php endif; ?>
+        </div>
+
+        <div class="last">
+          <?php if (!$this->driver->isCollection()) {
+            if ($snippet = $this->driver->getHighlightedSnippet()) {
+              if (!empty($snippet['caption'])) {
+                echo '<strong>' . $this->transEsc($snippet['caption']) . ':</strong> ';
+              }
+              if (!empty($snippet['snippet'])) {
+                echo '<span class="quotestart">&#8220;</span>...' . $this->highlight($snippet['snippet']) . '...<span class="quoteend">&#8221;</span><br/>';
+              }
+            }
+          } ?>
+
+          <?php /*fid_bbi #16050*/
+              $listTags = ($this->usertags()->getMode() !== 'disabled') ? $this->driver->getTags(
+                $list_id, $user_id, 'tag',$user_id
+              ) : array();
+          ?>
+
+          <?php $listNotes = $this->driver->getListNotes($list_id, $user_id); ?>
+          <?php if (count($listNotes) > 0): ?>
+            <strong><?=$this->transEsc('Notes')?>:</strong>
+            <?php if (count($listNotes) > 1): ?><br/><?php endif; ?>
+            <?php foreach ($listNotes as $note): ?>
+              <?=$this->escapeHtml($note)?><br/>
+            <?php endforeach; ?>
+          <?php endif; ?>
+
+          <?php if (count($this->lists) > 0): ?>
+            <strong><?=$this->transEsc('Saved in')?>:</strong>
+            <?php $i = 0;
+            foreach ($this->lists as $current): ?>
+              <a href="<?=$this->url('userList', array('id' => $current->id))?>"><?=$this->escapeHtml($current->title)?></a><?php if ($i++ < count($this->lists) - 1): ?>,<?php endif; ?>
+            <?php endforeach; ?>
+            <br/>
+          <?php endif; ?>
+
+          <div class="callnumAndLocation ajax-availability hidden">
+            <?php if ($this->driver->supportsAjaxStatus()): ?>
+              <strong class="hideIfDetailed"><?=$this->transEsc('Call Number')?>:</strong>
+              <span class="callnumber ajax-availability hidden">
+                <?=$this->transEsc('Loading')?>...<br/>
+              </span>
+              <strong><?=$this->transEsc('Located')?>:</strong>
+              <span class="location ajax-availability hidden">
+                <?=$this->transEsc('Loading')?>...
+              </span>
+              <div class="locationDetails"></div>
+            <?php else: ?>
+              <?php $summCallNo = $this->driver->getCallNumber();
+              if (!empty($summCallNo)): ?>
+                <strong><?=$this->transEsc('Call Number')?>:</strong> <?=$this->escapeHtml($summCallNo)?>
+              <?php endif; ?>
+            <?php endif; ?>
+          </div>
+
+          <?php /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive),
+                  but even if we don't plan to display the link, we still want to get the $openUrl
+                  value for use in generating a COinS (Z3988) tag -- see bottom of file.
+                */
+          $openUrl = $this->openUrl($this->driver, 'results');
+          $openUrlActive = $openUrl->isActive();
+          $doi = $this->doi($this->driver, 'results');
+          $doiActive = $doi->isActive();
+          // Account for replace_other_urls setting
+          $urls = $this->record($this->driver)->getLinkDetails($openUrlActive);
+
+          if ($openUrlActive || $doiActive || !empty($urls)):
+            ?>
+            <?php if ($openUrlActive): ?>
+            <br/>
+            <?=$openUrl->renderTemplate()?>
+          <?php endif; ?>
+
+            <?php if ($doiActive): ?>
+            <br/>
+            <?=$doi->renderTemplate()?>
+          <?php endif; ?>
+
+            <?php if (!is_array($urls)) {
+            $urls = [];
+          }
+            if (!$this->driver->isCollection()):
+              foreach ($urls as $current): ?>
+                <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>" class="fulltext" target="new"><i class="fa fa-external-link"
+                                                                                                                        aria-hidden="true"></i> <?=($current['url'] == $current['desc']) ? $this->transEsc('Get full text') : $this->escapeHtml($current['desc'])?>
+                </a>
+              <?php endforeach; ?>
+            <?php endif; ?>
+          <?php endif; ?>
+          <br/>
+
+          <?=$this->record($this->driver)->getFormatList()?>
+
+          <?php if (!$openUrlActive && empty($urls) && $this->driver->supportsAjaxStatus()): ?>
+            <span class="status ajax-availability hidden"><?=$this->transEsc('Loading')?>...</span>
+            <br/><br/>
+          <?php endif; ?>
+          <?=$this->record($this->driver)->getPreviews()?>
+        </div>
+      </div>
+
+      <div class="result-links hidden-print">
+        <?php /* finc: next line finc-specific; required to display public favorites lists, #12052, see also above - CK */ ?>
+        <?php if ($isEditable): ?>
+          <i class="fa fa-fw fa-edit" aria-hidden="true"></i> <a
+            href="<?=$this->url('myresearch-edit')?>?id=<?=urlencode($id)?>&amp;source=<?=urlencode($source)?><?php if (null !== $list_id): ?>&amp;list_id=<?=urlencode($list_id)?><?php endif; ?>"
+            class="edit tool"><?=$this->transEsc('Edit')?></a><br/>
+          <?php /* Use a different delete URL if we're removing from a specific list or the overall favorites: */
+          $deleteUrl = null === $list_id
+            ? $this->url('myresearch-favorites')
+            : $this->url('userList', ['id' => $list_id]);
+          $deleteUrlGet = $deleteUrl . '?delete=' . urlencode($id) . '&amp;source=' . urlencode($source);
+
+          $dLabel = 'delete-label-' . preg_replace('[\W]', '-', $id);
+          ?>
+          <div class="dropdown">
+            <i class="fa fa-fw fa-trash-o" aria-hidden="true"></i> <a class="dropdown-toggle" id="<?=$dLabel?>" role="button" data-toggle="dropdown" href="<?=$deleteUrlGet?>">
+              <?=$this->transEsc('Delete')?>
+            </a>
+            <ul class="dropdown-menu" role="menu" aria-labelledby="<?=$dLabel?>">
+              <li><a onClick="$.post('<?=$deleteUrl?>', {'delete':'<?=$this->escapeJs($id)?>','source':'<?=$this->escapeJs($source)?>','confirm':true},function(){location.reload(true)})"
+                     title="<?=$this->transEsc('confirm_delete_brief')?>"><?=$this->transEsc('confirm_dialog_yes')?></a></li>
+              <li><a><?=$this->transEsc('confirm_dialog_no')?></a></li>
+            </ul>
+          </div>
+
+          <?=$this->driver->supportsCoinsOpenUrl() ? '<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()) . '"></span>' : ''?>
+        <?php endif; ?>
+      </div>
+    </div>
+
+      <?php /*fid_bbi #16050*/ if (!empty($listTags)): ?>
+          <strong><?=$this->transEsc('Your Tags')?>:</strong>
+          <?php foreach ($listTags as $tag): ?>
+              <a href="<?=$this->currentPath() . $results->getUrlQuery()->addFacet('tags', $tag->tag)?>"><?=$this->escapeHtml($tag->tag)?></a>
+          <?php endforeach; ?>
+          <br/>
+      <?php endif; ?>
+
+    <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?>
+      <?=$thumbnail?>
+    <?php endif; ?>
+  </div>
+</div>
+<!-- fid_bbi: RecordDriver - DefaultRecord - list-entry - END -->
\ No newline at end of file
-- 
GitLab