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