File indexing completed on 2024-05-05 04:47:20

0001 /****************************************************************************************
0002  * Copyright (c) 2009 Téo Mrnjavac <teo@kde.org>                                        *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #include "OcsPersonItem.h"
0018 
0019 #include "core/support/Debug.h"
0020 
0021 #include <QAction>
0022 #include <KRun>
0023 #include <QStandardPaths>
0024 #include <Attica/Provider>
0025 #include <Attica/ItemJob>
0026 #include <KIO/OpenUrlJob>
0027 #include <KIO/StoredTransferJob>
0028 
0029 #include <QHBoxLayout>
0030 #include <QVBoxLayout>
0031 #include <QPainter>
0032 #include <QStyleOption>
0033 
0034 OcsPersonItem::OcsPersonItem( const KAboutPerson &person, const QString &ocsUsername, PersonStatus status, QWidget *parent )
0035     : QWidget( parent )
0036     , m_status( status )
0037     , m_state( Offline )
0038 {
0039     m_person = &person;
0040     m_ocsUsername = ocsUsername;
0041 
0042     setupUi( this );
0043     init();
0044 
0045     m_avatar->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
0046 
0047     //TODO: Add favorite artists!
0048 
0049 }
0050 
0051 void
0052 OcsPersonItem::init()
0053 {
0054     m_textLabel->setTextInteractionFlags( Qt::TextBrowserInteraction );
0055     m_textLabel->setOpenExternalLinks( true );
0056     m_textLabel->setContentsMargins( 5, 0, 0, 2 );
0057     m_verticalLayout->setSpacing( 0 );
0058 
0059     m_vertLine->hide();
0060     m_initialSpacer->changeSize( 0, 40, QSizePolicy::Fixed, QSizePolicy::Fixed );
0061     layout()->invalidate();
0062 
0063     m_aboutText.append( "<b>" + m_person->name() + "</b>" );
0064     if( !m_person->task().isEmpty() )
0065         m_aboutText.append( "<br/>" + m_person->task() );
0066 
0067     m_iconsBar = new KToolBar( this, false, false );
0068     m_snBar = new KToolBar( this, false, false );
0069 
0070     m_iconsBar->setIconSize( QSize( 22, 22 ) );
0071     m_iconsBar->setContentsMargins( 0, 0, 0, 0 );
0072     m_iconsBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
0073 
0074     if( m_status == Author )
0075     {
0076         QHBoxLayout *iconsLayout = new QHBoxLayout( this );
0077         iconsLayout->setMargin( 0 );
0078         iconsLayout->setSpacing( 0 );
0079         m_verticalLayout->insertLayout( m_verticalLayout->count() - 1, iconsLayout );
0080         iconsLayout->addWidget( m_iconsBar );
0081         iconsLayout->addWidget( m_snBar );
0082         iconsLayout->addStretch( 0 );
0083 
0084         m_snBar->setIconSize( QSize( 16, 16 ) );
0085         m_snBar->setContentsMargins( 0, 0, 0, 0 );
0086         m_snBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
0087     }
0088     else
0089     {
0090         layout()->addWidget( m_iconsBar );
0091         m_snBar->hide();
0092     }
0093 
0094     if( !m_person->emailAddress().isEmpty() )
0095     {
0096         QAction *email = new QAction( QIcon::fromTheme( QStringLiteral("mail-send") ), i18n("Email contributor"), this );
0097         email->setToolTip( m_person->emailAddress() );
0098         email->setData( QString( "mailto:" + m_person->emailAddress() ) );
0099         m_iconsBar->addAction( email );
0100     }
0101 
0102     if( !m_person->webAddress().isEmpty() )
0103     {
0104         QAction *homepage = new QAction( QIcon::fromTheme( QStringLiteral("internet-services") ), i18n("Visit contributor's homepage"), this );
0105         homepage->setToolTip( m_person->webAddress() );
0106         homepage->setData( m_person->webAddress() );
0107         m_iconsBar->addAction( homepage );
0108     }
0109 
0110     connect( m_iconsBar, &KToolBar::actionTriggered, this, &OcsPersonItem::launchUrl );
0111     connect( m_snBar, &KToolBar::actionTriggered, this, &OcsPersonItem::launchUrl );
0112     m_textLabel->setText( m_aboutText );
0113 }
0114 
0115 OcsPersonItem::~OcsPersonItem()
0116 {}
0117 
0118 QString
0119 OcsPersonItem::name() const
0120 {
0121     return m_person->name();
0122 }
0123 
0124 void
0125 OcsPersonItem::launchUrl( QAction *action ) //SLOT
0126 {
0127     QUrl url = QUrl( action->data().toString() );
0128     KIO::OpenUrlJob *openUrlJob = new KIO::OpenUrlJob(url, QStringLiteral("text/html"), this );
0129     openUrlJob->setRunExecutables( true );
0130     openUrlJob->start();
0131 }
0132 
0133 void
0134 OcsPersonItem::switchToOcs( Attica::Provider &provider )
0135 {
0136     if( m_state == Online )
0137         return;
0138     m_avatar->setFixedWidth( 56 );
0139     m_vertLine->show();
0140     m_initialSpacer->changeSize( 5, 40, QSizePolicy::Fixed, QSizePolicy::Fixed );
0141     layout()->invalidate();
0142 
0143     if( !m_ocsUsername.isEmpty() )
0144     {
0145         Attica::ItemJob< Attica::Person > *personJob;
0146         if( m_ocsUsername == QStringLiteral( "%%category%%" ) )   //TODO: handle grouping
0147             return;
0148 
0149         personJob = provider.requestPerson( m_ocsUsername );
0150         connect( personJob, &Attica::BaseJob::finished, this, &OcsPersonItem::onJobFinished );
0151         Q_EMIT ocsFetchStarted();
0152         m_state = Online;
0153         personJob->start();
0154     }
0155 }
0156 
0157 void
0158 OcsPersonItem::onJobFinished( Attica::BaseJob *job )
0159 {
0160     Attica::ItemJob< Attica::Person > *personJob = static_cast< Attica::ItemJob< Attica::Person > * >( job );
0161     Attica::Metadata metadata = personJob->metadata();
0162     if( metadata.error() == Attica::Metadata::NoError )
0163     {
0164         fillOcsData( personJob->result() );
0165     }
0166     Q_EMIT ocsFetchResult( metadata.error() );
0167 }
0168 
0169 void
0170 OcsPersonItem::fillOcsData( const Attica::Person &ocsPerson )
0171 {
0172     if( ocsPerson.avatarUrl().isValid() )
0173     {
0174         auto job = KIO::storedGet( ocsPerson.avatarUrl(), KIO::NoReload, KIO::HideProgressInfo );
0175         connect( job, &KIO::StoredTransferJob::result, this, &OcsPersonItem::onAvatarLoadingJobFinished );
0176     }
0177 
0178     if( !ocsPerson.country().isEmpty() )
0179     {
0180         m_aboutText.append( "<br/>" );
0181         if( !ocsPerson.city().isEmpty() )
0182             m_aboutText.append( i18nc( "A person's location: City, Country", "%1, %2", ocsPerson.city(), ocsPerson.country() ) );
0183         else
0184             m_aboutText.append( ocsPerson.country() );
0185     }
0186 
0187     if( m_status == Author )
0188     {
0189         if( !ocsPerson.extendedAttribute( QStringLiteral("ircchannels") ).isEmpty() )
0190         {
0191             QString channelsString = ocsPerson.extendedAttribute( QStringLiteral("ircchannels") );
0192             //We extract the channel names from the string provided by OCS:
0193             QRegExp channelrx = QRegExp( "#+[\\w\\.\\-\\/!()+]+([\\w\\-\\/!()+]?)", Qt::CaseInsensitive );
0194             QStringList channels;
0195             int pos = 0;
0196             while( ( pos = channelrx.indexIn( channelsString, pos ) ) != -1 )
0197             {
0198                 channels << channelrx.cap( 0 );
0199                 pos += channelrx.matchedLength();
0200             }
0201 
0202             m_aboutText.append( "<br/>" + i18n("IRC channels: ") );
0203             QString link;
0204             foreach( const QString &channel, channels )
0205             {
0206                 const QString channelName = QString( channel ).remove( '#' );
0207                 link = QStringLiteral( "irc://irc.freenode.org/%1" ).arg( channelName );
0208                 m_aboutText.append( QStringLiteral( "<a href=\"%1\">%2</a>" ).arg( link, channel ) + "  " );
0209             }
0210         }
0211         if( !ocsPerson.extendedAttribute( QStringLiteral("favouritemusic") ).isEmpty() )
0212         {
0213             QStringList artists = ocsPerson.extendedAttribute( QStringLiteral("favouritemusic") ).split( QStringLiteral(", ") );
0214             //TODO: make them clickable
0215             m_aboutText.append( "<br/>" + i18n( "Favorite music: " ) + artists.join( QStringLiteral(", ") ) );
0216         }
0217     }
0218 
0219     QAction *visitProfile = new QAction( QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, 
0220             "amarok/images/opendesktop-22.png" ) ) ), i18n( "Visit %1's openDesktop.org profile", ocsPerson.firstName() ), this );
0221 
0222     visitProfile->setToolTip( i18n( "Visit %1's profile on openDesktop.org", ocsPerson.firstName() ) );
0223 
0224     visitProfile->setData( ocsPerson.extendedAttribute( QStringLiteral("profilepage") ) );
0225     m_iconsBar->addAction( visitProfile );
0226 
0227     if( m_status == Author )
0228     {
0229         QVector< QPair< QString, QString > > ocsHomepages;
0230         ocsHomepages.append( QPair< QString, QString >( ocsPerson.extendedAttribute( QStringLiteral("homepagetype") ), ocsPerson.homepage() ) );
0231 
0232         debug() << "USER HOMEPAGE DATA STARTS HERE";
0233         debug() << ocsHomepages.last().first << " :: " << ocsHomepages.last().second;
0234 
0235         for( int i = 2; i <= 10; i++ )  //OCS supports 10 total homepages as of 2/oct/2009
0236         {
0237             QString type = ocsPerson.extendedAttribute( QStringLiteral( "homepagetype%1" ).arg( i ) );
0238             ocsHomepages.append( QPair< QString, QString >( ( type == QLatin1String("&nbsp;") ) ? QLatin1String("") : type,
0239                                                             ocsPerson.extendedAttribute( QStringLiteral( "homepage%1" ).arg( i ) ) ) );
0240             debug() << ocsHomepages.last().first << " :: " << ocsHomepages.last().second;
0241         }
0242 
0243         bool fillHomepageFromOcs = m_person->webAddress().isEmpty();    //We check if the person already has a homepage in KAboutPerson.
0244 
0245         for( QVector< QPair< QString, QString > >::const_iterator entry = ocsHomepages.constBegin();
0246              entry != ocsHomepages.constEnd(); ++entry )
0247         {
0248             QString type = (*entry).first;
0249             QString url = (*entry).second;
0250             QIcon icon;
0251             QString text;
0252 
0253             if( type == QLatin1String("Blog") )
0254             {
0255                 icon = QIcon::fromTheme( QStringLiteral("kblogger") );
0256                 text = i18n( "Visit contributor's blog" );
0257             }
0258             else if( type == QLatin1String("delicious") )
0259             {
0260                 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-delicious.png" ) ) );
0261                 text = i18n( "Visit contributor's del.icio.us profile" );
0262             }
0263             else if( type == QLatin1String("Digg") )
0264             {
0265                 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-digg.png" ) ) );
0266                 text = i18n( "Visit contributor's Digg profile" );
0267             }
0268             else if( type == QLatin1String("Facebook") )
0269             {
0270                 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-facebook.png" ) ) );
0271                 text = i18n( "Visit contributor's Facebook profile" );
0272             }
0273             else if( type == QLatin1String("Homepage") || type == QLatin1String("other") || ( type.isEmpty() && !url.isEmpty() ) )
0274             {
0275                 if( fillHomepageFromOcs )
0276                 {
0277                     QAction *homepage = new QAction( QIcon::fromTheme( QStringLiteral("internet-services") ), i18n("Visit contributor's homepage"), this );
0278                     homepage->setToolTip( url );
0279                     homepage->setData( url );
0280                     m_iconsBar->addAction( homepage );
0281                     fillHomepageFromOcs = false;
0282                     continue;
0283                 }
0284                 if( type == QLatin1String("other") && url.contains( QLatin1String("last.fm/") ) )     //HACK: assign a last.fm icon if the URL contains last.fm
0285                 {
0286                     icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-lastfm.png" ) ) );
0287                     text = i18n( "Visit contributor's Last.fm profile" );
0288                 }
0289                 else
0290                     continue;
0291             }
0292             else if( type == QLatin1String("LinkedIn") )
0293             {
0294                 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-linkedin.png" ) ) );
0295                 text = i18n( "Visit contributor's LinkedIn profile" );
0296             }
0297             else if( type == QLatin1String("MySpace") )
0298             {
0299                 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-myspace.png" ) ) );
0300                 text = i18n( "Visit contributor's MySpace homepage" );
0301             }
0302             else if( type == QLatin1String("Reddit") )
0303             {
0304                 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-reddit.png" ) ) );
0305                 text = i18n( "Visit contributor's Reddit profile" );
0306             }
0307             else if( type == QLatin1String("YouTube") )
0308             {
0309                 icon = QIcon( "dragonplayer" ); //FIXME: icon
0310                 text = i18n( "Visit contributor's YouTube profile" );
0311             }
0312             else if( type == QLatin1String("Twitter") )
0313             {
0314                 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-twitter.png" ) ) );
0315                 text = i18n( "Visit contributor's Twitter feed" );
0316             }
0317             else if( type == QLatin1String("Wikipedia") )
0318             {
0319                 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-wikipedia.png" ) ) );
0320                 text = i18n( "Visit contributor's Wikipedia profile" );
0321             }
0322             else if( type == QLatin1String("Xing") )
0323             {
0324                 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-xing.png" ) ) );
0325                 text = i18n( "Visit contributor's Xing profile" );
0326             }
0327             else if( type == QLatin1String("identi.ca") )
0328             {
0329                 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-identica.png" ) ) );
0330                 text = i18n( "Visit contributor's identi.ca feed" );
0331             }
0332             else if( type == QLatin1String("libre.fm") )
0333             {
0334                 icon = QIcon( "juk" );  //FIXME: icon
0335                 text = i18n( "Visit contributor's libre.fm profile" );
0336             }
0337             else if( type == QLatin1String("StackOverflow") )
0338             {
0339                 icon = QIcon( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/emblem-stackoverflow.png" ) ) );
0340                 text = i18n( "Visit contributor's StackOverflow profile" );
0341             }
0342             else
0343                 break;
0344             QAction *action = new QAction( icon, text, this );
0345             action->setToolTip( url );
0346             action->setData( url );
0347             m_snBar->addAction( action );
0348         }
0349 
0350         debug() << "END USER HOMEPAGE DATA";
0351     }
0352 
0353     m_textLabel->setText( m_aboutText );
0354 }
0355 
0356 void
0357 OcsPersonItem::onAvatarLoadingJobFinished( KJob *job )
0358 {
0359     auto storedJob = qobject_cast< KIO::StoredTransferJob * >( job );
0360     if( storedJob->error() )
0361     {
0362         debug() << "failed to download the avatar of" << m_ocsUsername << "error:" << storedJob->errorString();
0363         return;
0364     }
0365 
0366     QPixmap pic;
0367     if ( !pic.loadFromData( storedJob->data() ) )
0368     {
0369         debug() << "failed to load the avatar of" << m_ocsUsername;
0370         return;
0371     }
0372 
0373     m_avatar->setFixedSize( 56, 56 );
0374     m_avatar->setFrameShape( QFrame::StyledPanel ); //this is a FramedLabel, otherwise oxygen wouldn't paint the frame
0375     m_avatar->setPixmap( pic );
0376     m_avatar->setAlignment( Qt::AlignCenter );
0377 }
0378