Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 116
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
CargoBibtexFormat
0.00% covered (danger)
0.00%
0 / 116
0.00% covered (danger)
0.00%
0 / 6
2756
0.00% covered (danger)
0.00%
0 / 1
 allowedParameters
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 generateTitleKey
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
42
 generateAuthorKey
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
 generateEntryKey
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 generateBibtexEntries
0.00% covered (danger)
0.00%
0 / 76
0.00% covered (danger)
0.00%
0 / 1
1332
 queryAndDisplay
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2/**
3 * @author Tomás Bolaño
4 * @ingroup Cargo
5 */
6
7class CargoBibtexFormat extends CargoDeferredFormat {
8
9    public static function allowedParameters() {
10        return [
11            'default entry type' => [ 'type' => 'string' ],
12            'link text' => [ 'type' => 'string' ]
13        ];
14    }
15
16    /**
17     * Returns the first word from $title with a length equal or greater than
18     * $numChars. If no word exists, it repeats the process for $numChars-1,
19     * $numChars-2, etc., until a word is found. Before returning the word, it
20     * removes all non-alphabetic characters.
21     *
22     * @param string $title
23     * @param int $numChars
24     */
25    private static function generateTitleKey( $title, $numChars = 5 ) {
26        $titleKey = '';
27        if ( $title != '' && $numChars > 0 ) {
28            $titleWords = explode( ' ', str_replace( '-', ' ', strtolower( $title ) ) );
29            for ( $i = $numChars; $i > 0; $i-- ) {
30                foreach ( $titleWords as $titleWord ) {
31                    if ( strlen( $titleWord ) >= $i ) {
32                        $titleKey = preg_replace( "/[^a-zA-Z]/", '', $titleWord );
33                        break 2;
34                    }
35                }
36            }
37        }
38        return $titleKey;
39    }
40
41    /**
42     * Returns the last name of the first author. Before returning the last
43     * name, removes all non-alphabetic characters.
44     *
45     * @param string $author The list of authors in bibtex format.
46     */
47    private static function generateAuthorKey( $author ) {
48        $authorKey = '';
49        if ( $author != '' ) {
50            $authorList = explode( ' and ', $author );
51            $firstAuthor = trim( $authorList[0] );
52
53            // A bibtex author can be in different formats:
54            // - First von Last
55            // - von Last, First
56            // - von Last, Jr, First
57            // where von is a lowercase particle such as "de" or "de la"
58            if ( strpos( $firstAuthor, ',' ) != false ) {
59                $vonLast = trim( strtok( $firstAuthor, ',' ) );
60                $nameElems = explode( ' ', $vonLast );
61            } else {
62                $nameElems = explode( ' ', $firstAuthor );
63            }
64            $lastName = end( $nameElems );
65
66            // If the name has an hyphen ('-') we will keep only the first part
67            $authorKey = preg_replace( "/[^a-zA-Z]/", '', strtok( $lastName, '-' ) );
68            $authorKey = strtolower( $authorKey );
69        }
70
71        return $authorKey;
72    }
73
74    /**
75     * Returns a key for an entry given the author, the title, and the year.
76     */
77    private static function generateEntryKey( $author, $title, $year ) {
78        return self::generateAuthorKey( $author ) . $year . self::generateTitleKey( $title );
79    }
80
81    /**
82     * BibTeX month abbreviations
83     *
84     * @var string[]
85     */
86    private static $monthStrings = [ 'jan', 'feb', 'mar', 'apr', 'may',
87        'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec' ];
88
89    /**
90     * This is the list of BibTeX fields that do not have special cases for
91     * generating the output. Fields that have special cases are: title, author,
92     * editor, pages, month, and year.
93     *
94     * @var string[]
95     */
96    private static $bibtexFields = [ 'address', 'annote', 'booktitle',
97        'chapter', 'crossref', 'doi', 'edition', 'howpublished', 'institution',
98        'journal', 'key', 'note', 'number', 'organization', 'publisher',
99        'school', 'series', 'type', 'volume' ];
100
101    public static function generateBibtexEntries( $valuesTable, $fieldDescriptions, $displayParams ) {
102        $defaultEntryType = strtolower( $displayParams['default entry type'] ?? 'article' );
103
104        // We check here the existing fields so that we do not need later
105        // to call the array_key_exists function in the for loop
106        $bibtexkeyExists = array_key_exists( 'bibtexkey', $fieldDescriptions );
107        $entryTypeExists = array_key_exists( 'entrytype', $fieldDescriptions );
108
109        $dateExists = array_key_exists( 'date', $fieldDescriptions );
110        $yearExists = array_key_exists( 'year', $fieldDescriptions );
111        $monthExists = array_key_exists( 'month', $fieldDescriptions );
112
113        $authorExists = array_key_exists( 'author', $fieldDescriptions );
114        $editorExists = array_key_exists( 'editor', $fieldDescriptions );
115        $titleExists = array_key_exists( 'title', $fieldDescriptions );
116
117        $pagesExists = array_key_exists( 'pages', $fieldDescriptions );
118        $initialPageExists = array_key_exists( 'initialpage', $fieldDescriptions );
119        $lastPageExists = array_key_exists( 'lastpage', $fieldDescriptions );
120
121        // generate array of bibtex fields to output
122        $bibtexOutputFields = [];
123        foreach ( self::$bibtexFields as $bibtexField ) {
124            if ( array_key_exists( $bibtexField, $fieldDescriptions ) ) {
125                $bibtexOutputFields[] = $bibtexField;
126            }
127        }
128
129        // Define several strings that will be used to generate the output
130        $tabString = '  ';
131        $newlineString = "\n";
132        $bibtexEntryBeforeString = '';
133        $bibtexEntryAfterString = "\n\n";
134
135        $text = '';
136        foreach ( $valuesTable as $value ) {
137            if ( $entryTypeExists && $value['entrytype'] != '' ) {
138                $entryType = $value['entrytype'];
139            } else {
140                $entryType = $defaultEntryType;
141            }
142
143            // Obtain values for the fields year, month, author, editor, and title
144            if ( $dateExists && $value['date'] != '' ) {
145                $year = strtok( $value['date'], '-' );
146                if ( $value['date__precision'] <= 2 ) {
147                    $month = ltrim( strtok( '-' ), '0' );
148                } else {
149                    $month = '';
150                }
151            } else {
152                $year = $yearExists ? $value['year'] : '';
153                $month = $monthExists ? $value['month'] : '';
154            }
155
156            if ( $authorExists && $value['author'] != '' ) {
157                if ( $fieldDescriptions['author']->mIsList ) {
158                    $delimiter = $fieldDescriptions['author']->getDelimiter();
159                    $author = str_replace( $delimiter, ' and ', $value['author'] );
160                } else {
161                    $author = $value['author'];
162                }
163            } else {
164                $author = '';
165            }
166
167            if ( $editorExists && $value['editor'] != '' ) {
168                if ( $fieldDescriptions['editor']->mIsList ) {
169                    $delimiter = $fieldDescriptions['editor']->getDelimiter();
170                    $editor = str_replace( $delimiter, ' and ', $value['editor'] );
171                } else {
172                    $editor = $value['editor'];
173                }
174            } else {
175                $editor = '';
176            }
177
178            $title = $titleExists ? $value['title'] : '';
179
180            // Generate the entry header (entry type and key)
181            $text .= $bibtexEntryBeforeString;
182            $text .= '@' . $entryType . '{';
183            if ( $bibtexkeyExists && $value['bibtexkey'] != '' ) {
184                $text .= $value['bibtexkey'];
185            } else {
186                $text .= self::generateEntryKey( $author, $title, $year );
187            }
188            $text .= ',' . $newlineString;
189
190            // Generate title, author, and editor fields
191            if ( $title != '' ) {
192                $text .= $tabString . 'title={' . $title . '},' . $newlineString;
193            }
194
195            if ( $author != '' ) {
196                $text .= $tabString . 'author={' . $author . '},' . $newlineString;
197            }
198
199            if ( $editor != '' ) {
200                $text .= $tabString . 'editor={' . $editor . '},' . $newlineString;
201            }
202
203            // Generate remaining fields (except pages, year, and month)
204            foreach ( $bibtexOutputFields as $bibtexOutputField ) {
205                if ( $value[$bibtexOutputField] != '' ) {
206                    $text .= $tabString . $bibtexOutputField . '={' . $value[$bibtexOutputField] . '},' . $newlineString;
207                }
208            }
209
210            // Generate pages, year, and month fields
211            if ( $pagesExists && $value['pages'] != '' ) {
212                $text .= $tabString . 'pages={' . $value['pages'] . '}' . $newlineString;
213            } elseif ( $initialPageExists && $lastPageExists && $value['initialpage'] != '' ) {
214                $pages = $value['initialpage'];
215                if ( $value['lastpage'] != '' && $value['initialpage'] != $value['lastpage'] ) {
216                    $pages .= '--' . $value['lastpage'];
217                }
218                $text .= $tabString . 'pages={' . $pages . '},' . $newlineString;
219            }
220
221            if ( $year != '' ) {
222                $text .= $tabString . 'year={' . $year . '},' . $newlineString;
223            }
224
225            if ( $month != '' ) {
226                // For the month field, if it is passed as a number between 1
227                // and 12 we use the three letter month abbreviation (i.e., jan,
228                // feb, mar, etc.). If it is passed as any other string, then
229                // that string will be used.
230                if ( $month >= 1 && $month <= 12 ) {
231                    $text .= $tabString . 'month=' . self::$monthStrings[$month - 1] . ',' . $newlineString;
232                } else {
233                    $text .= $tabString . 'month={' . $month . '},' . $newlineString;
234                }
235            }
236
237            $text .= '}';
238            $text .= $bibtexEntryAfterString;
239        }
240
241        return $text;
242    }
243
244    /**
245     * This function creates a link for the query. The query and the bibtex
246     * generation are actually executed by the function displayBibtexData of the
247     * CargoExport class.
248     *
249     * @param array $sqlQueries
250     * @param array $displayParams
251     * @param array|null $querySpecificParams Unused
252     * @return string HTML
253     */
254    public function queryAndDisplay( $sqlQueries, $displayParams, $querySpecificParams = null ) {
255        $ce = SpecialPage::getTitleFor( 'CargoExport' );
256        $queryParams = $this->sqlQueriesToQueryParams( $sqlQueries );
257        $queryParams['format'] = 'bibtex';
258        if ( array_key_exists( 'default entry type', $displayParams ) && $displayParams['default entry type'] != '' ) {
259            $queryParams['default entry type'] = $displayParams['default entry type'];
260        }
261        if ( array_key_exists( 'link text', $displayParams ) && $displayParams['link text'] != '' ) {
262            $linkText = $displayParams['link text'];
263        } else {
264            $linkText = wfMessage( 'cargo-viewbibtex' )->text();
265        }
266        $linkAttrs = [
267            'href' => $ce->getFullURL( $queryParams ),
268            'target' => '_blank' // link will open in a new tab
269        ];
270        $text = Html::rawElement( 'a', $linkAttrs, $linkText );
271
272        return $text;
273    }
274
275}