1 /***************************************************************************
2     qgslayermetadataformatter.cpp
3     ---------------------
4     begin                : September 2017
5     copyright            : (C) 2017 by Etienne Trimaille
6     email                : etienne.trimaille at gmail dot com
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 #include <QStringBuilder>
16 #include <QDateTime>
17 
18 #include "qgslayermetadataformatter.h"
19 #include "qgslayermetadata.h"
20 #include "qgsstringutils.h"
21 
QgsLayerMetadataFormatter(const QgsLayerMetadata & metadata)22 QgsLayerMetadataFormatter::QgsLayerMetadataFormatter( const QgsLayerMetadata &metadata )
23   : mMetadata( metadata )
24 {
25 }
26 
accessSectionHtml() const27 QString QgsLayerMetadataFormatter::accessSectionHtml() const
28 {
29   QString myMetadata = QStringLiteral( "<table class=\"list-view\">\n" );
30   myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Fees" ) + QStringLiteral( "</td><td>" ) + QgsStringUtils::insertLinks( mMetadata.fees() ) + QStringLiteral( "</td></tr>\n" );
31   myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Licenses" ) + QStringLiteral( "</td><td>" ) + QgsStringUtils::insertLinks( mMetadata.licenses().join( QLatin1String( "<br />" ) ) ) + QStringLiteral( "</td></tr>\n" );
32   myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Rights" ) + QStringLiteral( "</td><td>" ) + QgsStringUtils::insertLinks( mMetadata.rights().join( QLatin1String( "<br />" ) ) ) + QStringLiteral( "</td></tr>\n" );
33   myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Constraints" ) + QStringLiteral( "</td><td>" );
34   const QList<QgsLayerMetadata::Constraint> &constraints = mMetadata.constraints();
35   bool notFirstRow = false;
36   for ( const QgsLayerMetadata::Constraint &constraint : constraints )
37   {
38     if ( notFirstRow )
39     {
40       myMetadata += QLatin1String( "<br />" );
41     }
42     myMetadata += QStringLiteral( "<strong>" ) + constraint.type + QStringLiteral( ": </strong>" ) + QgsStringUtils::insertLinks( constraint.constraint );
43     notFirstRow = true;
44   }
45   myMetadata += QLatin1String( "</td></tr>\n" );
46   myMetadata += QLatin1String( "</table>\n" );
47   return myMetadata;
48 }
49 
contactsSectionHtml() const50 QString QgsLayerMetadataFormatter::contactsSectionHtml() const
51 {
52   const QList<QgsAbstractMetadataBase::Contact> &contacts = mMetadata.contacts();
53   QString myMetadata;
54   if ( contacts.isEmpty() )
55   {
56     myMetadata += QStringLiteral( "<p>" ) + tr( "No contact yet." ) + QStringLiteral( "</p>" );
57   }
58   else
59   {
60     myMetadata += QLatin1String( "<table width=\"100%\" class=\"tabular-view\">\n" );
61     myMetadata += QLatin1String( "<tr><th>" ) + tr( "ID" ) + QLatin1String( "</th><th>" ) + tr( "Name" ) + QLatin1String( "</th><th>" ) + tr( "Position" ) + QLatin1String( "</th><th>" ) + tr( "Organization" ) + QLatin1String( "</th><th>" ) + tr( "Role" ) + QLatin1String( "</th><th>" ) + tr( "Email" ) + QLatin1String( "</th><th>" ) + tr( "Voice" ) + QLatin1String( "</th><th>" ) + tr( "Fax" ) + QLatin1String( "</th><th>" ) + tr( "Addresses" ) + QLatin1String( "</th></tr>\n" );
62     int i = 1;
63     for ( const QgsAbstractMetadataBase::Contact &contact : contacts )
64     {
65       QString rowClass;
66       if ( i % 2 )
67         rowClass = QStringLiteral( "class=\"odd-row\"" );
68       myMetadata += QLatin1String( "<tr " ) + rowClass + QLatin1String( "><td>" ) + QString::number( i ) + QLatin1String( "</td><td>" ) + contact.name + QLatin1String( "</td><td>" ) + contact.position + QLatin1String( "</td><td>" ) + contact.organization + QLatin1String( "</td><td>" ) + contact.role + QStringLiteral( "</td><td><a href=\"mailto:%1\">%1</a></td><td>" ).arg( contact.email ) + contact.voice + QLatin1String( "</td><td>" ) + contact.fax + QLatin1String( "</td><td>" );
69       bool notFirstRow = false;
70       for ( const QgsAbstractMetadataBase::Address &oneAddress : contact.addresses )
71       {
72         if ( notFirstRow )
73         {
74           myMetadata += QLatin1String( "<br />\n" );
75         }
76         if ( ! oneAddress.type.isEmpty() )
77         {
78           myMetadata += oneAddress.type + QStringLiteral( "<br />" );
79         }
80         if ( ! oneAddress.address.isEmpty() )
81         {
82           myMetadata += oneAddress.address + QStringLiteral( "<br />" );
83         }
84         if ( ! oneAddress.postalCode.isEmpty() )
85         {
86           myMetadata += oneAddress.postalCode + QStringLiteral( "<br />" );
87         }
88         if ( ! oneAddress.city.isEmpty() )
89         {
90           myMetadata += oneAddress.city + QStringLiteral( "<br />" );
91         }
92         if ( ! oneAddress.administrativeArea.isEmpty() )
93         {
94           myMetadata += oneAddress.administrativeArea + QStringLiteral( "<br />" );
95         }
96         if ( ! oneAddress.country.isEmpty() )
97         {
98           myMetadata += oneAddress.country;
99         }
100         notFirstRow = true;
101       }
102       myMetadata += QLatin1String( "</td></tr>\n" );
103       i++;
104     }
105     myMetadata += QLatin1String( "</table>\n" );
106   }
107   return myMetadata;
108 }
109 
extentSectionHtml(const bool showSpatialExtent) const110 QString QgsLayerMetadataFormatter::extentSectionHtml( const bool showSpatialExtent ) const
111 {
112   const QgsLayerMetadata::Extent extent = mMetadata.extent();
113   bool notFirstRow = false;
114   QString myMetadata = QStringLiteral( "<table class=\"list-view\">\n" );
115   if ( showSpatialExtent )
116   {
117     myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "CRS" ) + QStringLiteral( "</td><td>" );
118     if ( mMetadata.crs().isValid() )
119     {
120       myMetadata += mMetadata.crs().userFriendlyIdentifier() + QStringLiteral( " - " );
121       if ( mMetadata.crs().isGeographic() )
122         myMetadata += tr( "Geographic" );
123       else
124         myMetadata += tr( "Projected" );
125     }
126     myMetadata += QLatin1String( "</td></tr>\n" );
127 
128     myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Spatial Extent" ) + QStringLiteral( "</td><td>" );
129     const QList< QgsLayerMetadata::SpatialExtent > spatialExtents = extent.spatialExtents();
130     for ( const QgsLayerMetadata::SpatialExtent &spatialExtent : spatialExtents )
131     {
132       if ( notFirstRow )
133       {
134         myMetadata += QLatin1String( "<br />\n" );
135       }
136       myMetadata += QStringLiteral( "<strong>" ) + tr( "CRS" ) + QStringLiteral( ": </strong>" ) + spatialExtent.extentCrs.userFriendlyIdentifier() + QStringLiteral( " - " );
137       if ( spatialExtent.extentCrs.isGeographic() )
138         myMetadata += tr( "Geographic" );
139       else
140         myMetadata += tr( "Projected" );
141       myMetadata += QLatin1String( "<br />" );
142       myMetadata += QStringLiteral( "<strong>" ) + tr( "X Minimum:" ) + QStringLiteral( " </strong>" ) +  qgsDoubleToString( spatialExtent.bounds.xMinimum() ) + QStringLiteral( "<br />" );
143       myMetadata += QStringLiteral( "<strong>" ) + tr( "Y Minimum:" ) + QStringLiteral( " </strong>" ) +  qgsDoubleToString( spatialExtent.bounds.yMinimum() ) + QStringLiteral( "<br />" );
144       myMetadata += QStringLiteral( "<strong>" ) + tr( "X Maximum:" ) + QStringLiteral( " </strong>" ) +  qgsDoubleToString( spatialExtent.bounds.xMaximum() ) + QStringLiteral( "<br />" );
145       myMetadata += QStringLiteral( "<strong>" ) + tr( "Y Maximum:" ) + QStringLiteral( " </strong>" ) +  qgsDoubleToString( spatialExtent.bounds.yMaximum() ) + QStringLiteral( "<br />" );
146       if ( spatialExtent.bounds.zMinimum() || spatialExtent.bounds.zMaximum() )
147       {
148         myMetadata += QStringLiteral( "<strong>" ) + tr( "Z Minimum:" ) + QStringLiteral( " </strong>" ) +  qgsDoubleToString( spatialExtent.bounds.zMinimum() ) + QStringLiteral( "<br />" );
149         myMetadata += QStringLiteral( "<strong>" ) + tr( "Z Maximum:" ) + QStringLiteral( " </strong>" ) +  qgsDoubleToString( spatialExtent.bounds.zMaximum() );
150       }
151       notFirstRow = true;
152     }
153     myMetadata += QLatin1String( "</td></tr>\n" );
154   }
155   myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Temporal Extent" ) + QStringLiteral( "</td><td>" );
156   const QList< QgsDateTimeRange > temporalExtents = extent.temporalExtents();
157   notFirstRow = false;
158   for ( const QgsDateTimeRange &temporalExtent : temporalExtents )
159   {
160     if ( notFirstRow )
161     {
162       myMetadata += QLatin1String( "<br />\n" );
163     }
164     if ( temporalExtent.isInstant() )
165     {
166       myMetadata += QStringLiteral( "<strong>" ) + tr( "Instant:" ) + QStringLiteral( " </strong>" ) + temporalExtent.begin().toTimeSpec( Qt::OffsetFromUTC ).toString( Qt::ISODate );
167     }
168     else
169     {
170       myMetadata += QStringLiteral( "<strong>" ) + tr( "Start:" ) + QStringLiteral( " </strong>" ) + temporalExtent.begin().toTimeSpec( Qt::OffsetFromUTC ).toString( Qt::ISODate ) + QStringLiteral( "<br />\n" );
171       myMetadata += QStringLiteral( "<strong>" ) + tr( "End:" ) + QStringLiteral( " </strong>" ) + temporalExtent.end().toTimeSpec( Qt::OffsetFromUTC ).toString( Qt::ISODate );
172     }
173     notFirstRow = true;
174   }
175   myMetadata += QLatin1String( "</td></tr>\n" );
176   myMetadata += QLatin1String( "</table>\n" );
177   return myMetadata;
178 }
179 
identificationSectionHtml() const180 QString QgsLayerMetadataFormatter::identificationSectionHtml() const
181 {
182   QString myMetadata = QStringLiteral( "<table class=\"list-view\">\n" );
183 
184   // Identifier
185   myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Identifier" ) + QStringLiteral( "</td><td>" ) + mMetadata.identifier() + QStringLiteral( "</td></tr>\n" );
186 
187   // Parent Identifier
188   myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Parent Identifier" ) + QStringLiteral( "</td><td>" ) + mMetadata.parentIdentifier() + QStringLiteral( "</td></tr>\n" );
189 
190   // Title
191   myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Title" ) + QStringLiteral( "</td><td>" ) + mMetadata.title() + QStringLiteral( "</td></tr>\n" );
192 
193   // Type
194   myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Type" ) + QStringLiteral( "</td><td>" ) + mMetadata.type() + QStringLiteral( "</td></tr>\n" );
195 
196   // Language
197   myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Language" ) + QStringLiteral( "</td><td>" ) + mMetadata.language() + QStringLiteral( "</td></tr>\n" );
198 
199   // Abstract
200   myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Abstract" ) + QStringLiteral( "</td><td>" ) + QgsStringUtils::insertLinks( mMetadata.abstract() ).replace( '\n', QLatin1String( "<br>" ) ) + QStringLiteral( "</td></tr>\n" );
201 
202   // Categories
203   myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Categories" ) + QStringLiteral( "</td><td>" ) + mMetadata.categories().join( QLatin1String( ", " ) ) + QStringLiteral( "</td></tr>\n" );
204 
205   // Keywords
206   myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Keywords" ) + QStringLiteral( "</td><td>\n" );
207   QMapIterator<QString, QStringList> i( mMetadata.keywords() );
208   if ( i.hasNext() )
209   {
210     myMetadata += QLatin1String( "<table width=\"100%\" class=\"tabular-view\">\n" );
211     myMetadata += QLatin1String( "<tr><th>" ) + tr( "Vocabulary" ) + QLatin1String( "</th><th>" ) + tr( "Items" ) + QLatin1String( "</th></tr>\n" );
212     int j = 1;
213     while ( i.hasNext() )
214     {
215       i.next();
216       QString rowClass;
217       if ( j % 2 )
218         rowClass = QStringLiteral( "class=\"odd-row\"" );
219       myMetadata += QLatin1String( "<tr " ) + rowClass + QLatin1String( "><td>" ) + i.key() + QLatin1String( "</td><td>" ) + i.value().join( QLatin1String( ", " ) ) + QLatin1String( "</td></tr>\n" );
220       j++;
221     }
222     myMetadata += QLatin1String( "</table>\n" ); // End keywords table
223   }
224   myMetadata += QLatin1String( "</td></tr>\n" ); // End of keywords row
225   myMetadata += QLatin1String( "</table>\n" ); // End identification table
226   return myMetadata;
227 }
228 
historySectionHtml() const229 QString QgsLayerMetadataFormatter::historySectionHtml() const
230 {
231   QString myMetadata;
232   const QStringList historyItems = mMetadata.history();
233   if ( historyItems.isEmpty() )
234   {
235     myMetadata += QStringLiteral( "<p>" ) + tr( "No history yet." ) + QStringLiteral( "</p>\n" );
236   }
237   else
238   {
239     myMetadata += QLatin1String( "<table width=\"100%\" class=\"tabular-view\">\n" );
240     myMetadata += QLatin1String( "<tr><th>" ) + tr( "ID" ) + QLatin1String( "</th><th>" ) + tr( "Action" ) + QLatin1String( "</th></tr>\n" );
241     int i = 1;
242     for ( const QString &history : historyItems )
243     {
244       QString rowClass;
245       if ( i % 2 )
246         rowClass = QStringLiteral( "class=\"odd-row\"" );
247       myMetadata += QLatin1String( "<tr " ) + rowClass + QLatin1String( "><td width=\"5%\">" ) + QString::number( i ) + QLatin1String( "</td><td>" ) + QgsStringUtils::insertLinks( history ) + QLatin1String( "</td></tr>\n" );
248       i++;
249     }
250     myMetadata += QLatin1String( "</table>\n" );
251   }
252   return myMetadata;
253 }
254 
linksSectionHtml() const255 QString QgsLayerMetadataFormatter::linksSectionHtml() const
256 {
257   QString myMetadata;
258   const QList<QgsAbstractMetadataBase::Link> &links = mMetadata.links();
259   if ( links.isEmpty() )
260   {
261     myMetadata += QStringLiteral( "<p>" ) + tr( "No links yet." ) + QStringLiteral( "</p>\n" );
262   }
263   else
264   {
265     myMetadata += QLatin1String( "<table width=\"100%\" class=\"tabular-view\">\n" );
266     myMetadata += QLatin1String( "<tr><th>" ) + tr( "ID" ) + QLatin1String( "</th><th>" ) + tr( "Name" ) + QLatin1String( "</th><th>" ) + tr( "Type" ) + QLatin1String( "</th><th>" ) + tr( "URL" ) + QLatin1String( "</th><th>" ) + tr( "Description" ) + QLatin1String( "</th><th>" ) + tr( "Format" ) + QLatin1String( "</th><th>" ) + tr( "MIME Type" ) + QLatin1String( "</th><th>" ) + tr( "Size" ) + QLatin1String( "</th></tr>\n" );
267     int i = 1;
268     for ( const QgsAbstractMetadataBase::Link &link : links )
269     {
270       QString rowClass;
271       if ( i % 2 )
272         rowClass = QStringLiteral( "class=\"odd-row\"" );
273       myMetadata += QLatin1String( "<tr " ) + rowClass + QLatin1String( "><td>" ) + QString::number( i ) + QLatin1String( "</td><td>" ) + link.name + QLatin1String( "</td><td>" ) + link.type + QStringLiteral( "</td><td><a href=\"%1\">%1</a></td><td>" ).arg( link.url ) + link.description + QLatin1String( "</td><td>" ) + link.format + QLatin1String( "</td><td>" ) + link.mimeType + QLatin1String( "</td><td>" ) + link.size + QLatin1String( "</td></tr>\n" );
274       i++;
275     }
276     myMetadata += QLatin1String( "</table>\n" );
277   }
278   return myMetadata;
279 }
280