423 lines
12 KiB
PHP
423 lines
12 KiB
PHP
<?php
|
|
|
|
namespace app\models;
|
|
|
|
use app\components\Git;
|
|
use app\components\Serialize;
|
|
use app\models\query\PackageQuery;
|
|
use bigpaulie\fancybox\FancyBox;
|
|
use cebe\markdown\GithubMarkdown;
|
|
use Github\Client;
|
|
use Yii;
|
|
use yii\behaviors\TimestampBehavior;
|
|
use yii\db\ActiveRecord;
|
|
use yii\helpers\Html;
|
|
use yii\widgets\Menu;
|
|
|
|
/**
|
|
* This is the model class for table "{{%package}}".
|
|
*
|
|
* @property integer $id
|
|
* @property string $name
|
|
* @property string $url
|
|
* @property integer $hits
|
|
* @property string $bower
|
|
* @property string $readme
|
|
* @property string $readme_format
|
|
* @property string $description
|
|
* @property string $homepage
|
|
* @property string $keywords
|
|
* @property string|array $authors
|
|
* @property string|array $screenshots
|
|
* @property string|array $license
|
|
* @property string $created_at
|
|
* @property string $updated_at
|
|
*/
|
|
class Package extends ActiveRecord
|
|
{
|
|
/**
|
|
* @var array
|
|
*/
|
|
public $serializeAttributes = ['bower', 'screenshots', 'authors', 'license'];
|
|
|
|
/**
|
|
* @var bool
|
|
*/
|
|
public $serialized = false;
|
|
|
|
/**
|
|
* @var bool
|
|
*/
|
|
public $checkUrl = true;
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
public static function tableName()
|
|
{
|
|
return '{{%package}}';
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
public function rules()
|
|
{
|
|
return [
|
|
[['name', 'url'], 'required'],
|
|
[['name'], 'string', 'max' => 50],
|
|
[['url'], 'string', 'max' => 255],
|
|
[['name', 'url'], 'unique'],
|
|
[['name', 'url'], 'trim'],
|
|
[['name'], 'match', 'pattern' => '/^[-a-z0-9_]+$/', 'message' => '{attribute} can only contain lowercase letters, numbers, "_" and "-"'],
|
|
[['url'], 'match', 'pattern' => '%(git|http(s)?)(:(//)?)([\w./\-~]+)(\.git)%', 'message' => '{attribute} must be a valid git endpoint.'],
|
|
[['url'], function ($attribute) {
|
|
if ($this->checkUrl && Git::getFile($this->$attribute) === false) {
|
|
$this->addError($attribute, 'Could not fetch remote repository.');
|
|
}
|
|
}],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
public function attributeLabels()
|
|
{
|
|
return [
|
|
'id' => 'ID',
|
|
'name' => 'Name',
|
|
'url' => 'URL',
|
|
'hits' => 'Hits',
|
|
'bower' => 'Bower',
|
|
'readme' => 'Readme',
|
|
'description' => 'Description',
|
|
'homepage' => 'Homepage',
|
|
'keywords' => 'Keywords',
|
|
'authors' => 'Authors',
|
|
'license' => 'License',
|
|
'screenshots' => 'Screenshots',
|
|
'created_at' => 'Created',
|
|
'updated_at' => 'Updated',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
public function behaviors()
|
|
{
|
|
return [
|
|
[
|
|
'class' => TimestampBehavior::className(),
|
|
'value' => function () {
|
|
return date('Y-m-d H:i:s');
|
|
},
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
* @return PackageQuery the active query used by this AR class.
|
|
*/
|
|
public static function find()
|
|
{
|
|
return new PackageQuery(get_called_class());
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
public function afterFind()
|
|
{
|
|
$this->serialized = true;
|
|
$this->unserializeAttributes();
|
|
parent::afterFind();
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
public function beforeSave($insert)
|
|
{
|
|
$this->serializeAttributes();
|
|
return parent::beforeSave($insert);
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
public function afterSave($insert, $changedAttributes)
|
|
{
|
|
$this->unserializeAttributes();
|
|
parent::afterSave($insert, $changedAttributes);
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
public function getDirtyAttributes($names = null)
|
|
{
|
|
$this->serializeAttributes();
|
|
$attributes = parent::getDirtyAttributes($names);
|
|
$this->unserializeAttributes();
|
|
return $attributes;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public function serializeAttributes()
|
|
{
|
|
if (!$this->serialized) {
|
|
foreach ($this->serializeAttributes as $attribute) {
|
|
if ($this->$attribute) {
|
|
$this->$attribute = [Serialize::serialize($this->$attribute), \PDO::PARAM_LOB];
|
|
}
|
|
}
|
|
$this->serialized = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public function unserializeAttributes()
|
|
{
|
|
if ($this->serialized) {
|
|
foreach ($this->serializeAttributes as $attribute) {
|
|
if ($this->$attribute) {
|
|
$this->$attribute = Serialize::unserialize($this->$attribute);
|
|
}
|
|
}
|
|
$this->serialized = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function harvestModInfo()
|
|
{
|
|
// fetch bower.json
|
|
$this->bower = Git::getFile($this->url, 'bower.json');
|
|
if ($this->bower) {
|
|
$this->bower = json_decode($this->bower, true);
|
|
// set fields from bower
|
|
if (isset($this->bower['description'])) {
|
|
$this->description = substr($this->bower['description'], 0, 140);
|
|
}
|
|
if (isset($this->bower['homepage'])) {
|
|
$this->homepage = $this->bower['homepage'];
|
|
}
|
|
if (isset($this->bower['keywords'])) {
|
|
$this->keywords = implode(',', $this->bower['keywords']);
|
|
}
|
|
if (isset($this->bower['authors'])) {
|
|
$this->authors = $this->bower['authors'];
|
|
}
|
|
if (isset($this->bower['screenshots'])) {
|
|
$this->screenshots = $this->bower['screenshots'];
|
|
}
|
|
if (isset($this->bower['license'])) {
|
|
$this->license = $this->bower['license'];
|
|
}
|
|
} else {
|
|
// no bower, get from github api
|
|
if (strpos($this->url, 'github.com')) {
|
|
$url = parse_url(Git::getUrl($this->url));
|
|
$path = explode('/', trim($url['path'], '/'));
|
|
$github = new Client();
|
|
$github->authenticate(getenv('GITHUB_TOKEN'), Client::AUTH_URL_TOKEN);
|
|
$repo = $github->api('repo')->show($path[0], $path[1]);
|
|
if (isset($repo['description'])) {
|
|
$this->description = substr($repo['description'], 0, 140);
|
|
}
|
|
if (isset($repo['homepage'])) {
|
|
$this->homepage = $repo['homepage'];
|
|
} elseif (isset($repo['html_url'])) {
|
|
$this->homepage = $repo['html_url'];
|
|
}
|
|
if (isset($repo['owner']['login'])) {
|
|
$this->authors = [$repo['owner']['login']];
|
|
}
|
|
}
|
|
}
|
|
|
|
// update authors
|
|
if (!$this->authors) {
|
|
$url = parse_url(Git::getUrl($this->url));
|
|
$path = explode('/', trim($url['path'], '/'));
|
|
$this->authors = [$path[0]];
|
|
}
|
|
|
|
// fetch readme
|
|
foreach (['README.md', 'readme.md', 'Readme.md'] as $file) {
|
|
$this->readme = Git::getFile($this->url, $file);
|
|
if ($this->readme) {
|
|
$this->readme_format = 'markdown';
|
|
break;
|
|
}
|
|
}
|
|
if (!$this->readme) {
|
|
foreach (['README.txt', 'readme.txt', 'Readme.txt', 'README', 'readme'] as $file) {
|
|
$this->readme = Git::getFile($this->url, $file);
|
|
if ($this->readme) {
|
|
$this->readme_format = 'text';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getScreenshotsHtml()
|
|
{
|
|
$screenshots = [];
|
|
if ($this->screenshots) {
|
|
foreach ($this->screenshots as $screenshot) {
|
|
$screenshots[] = FancyBox::widget([
|
|
'type' => 'image',
|
|
'item' => [
|
|
'href' => $screenshot,
|
|
'src' => $screenshot,
|
|
],
|
|
'htmlOptions' => [
|
|
'imageOptions' => [
|
|
'class' => 'thumbnail',
|
|
'style' => 'max-width:100%',
|
|
],
|
|
],
|
|
]);
|
|
}
|
|
}
|
|
return $screenshots ? implode('', $screenshots) : null;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getAuthorsHtml()
|
|
{
|
|
$authors = [];
|
|
if ($this->authors) {
|
|
foreach ($this->authors as $author) {
|
|
$authors[] = '<li>' . $this->formatAuthor($author) . '</li>';
|
|
}
|
|
}
|
|
return $authors ? '<ul class="list-unstyled" style="margin-bottom:0">' . implode('', $authors) . '</ul>' : null;
|
|
}
|
|
|
|
/**
|
|
* @param $author
|
|
* @return string
|
|
*/
|
|
public function formatAuthor($author)
|
|
{
|
|
$authorInfo = [];
|
|
if (is_array($author)) {
|
|
if (isset($author['name'])) {
|
|
$authorInfo['name'] = $author['name'];
|
|
}
|
|
if (isset($author['email'])) {
|
|
$authorInfo['email'] = Html::a($author['email'], 'mailto:' . $author['email']);
|
|
}
|
|
if (isset($author['homepage'])) {
|
|
$authorInfo['homepage'] = Html::a($author['homepage'], $author['homepage']);
|
|
}
|
|
return implode(' ', $authorInfo);
|
|
} else {
|
|
// https://github.com/jonschlinkert/author-regex/blob/master/index.js
|
|
preg_match_all('/^([^<(]+?)?[ \t]*(?:<([^>(]+?)>)?[ \t]*(?:\(([^)]+?)\)|$)/', $author, $result, PREG_SET_ORDER);
|
|
if (isset($result[0][1])) {
|
|
$authorInfo['name'] = $result[0][1];
|
|
}
|
|
if (isset($result[0][2])) {
|
|
$authorInfo['email'] = Html::a($result[0][2], 'mailto:' . $result[0][2]);
|
|
}
|
|
if (isset($result[0][3])) {
|
|
$authorInfo['homepage'] = Html::a($result[0][3], $result[0][3]);
|
|
}
|
|
}
|
|
return implode(' ', $authorInfo);
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getLicenseHtml()
|
|
{
|
|
$licenses = [];
|
|
if ($this->license) {
|
|
if (is_array($this->license)) {
|
|
foreach ($this->license as $license) {
|
|
$licenses[] = $license;
|
|
}
|
|
} else {
|
|
$licenses[] = $this->license;
|
|
}
|
|
}
|
|
return $licenses ? implode('<br>', $licenses) : null;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getLinksHtml()
|
|
{
|
|
$items = [];
|
|
if ($this->homepage && $this->homepage != Git::getUrl($this->url)) {
|
|
$items[] = ['label' => 'Home', 'url' => $this->homepage, 'active' => true];
|
|
}
|
|
$items[] = ['label' => 'Repository', 'url' => Git::getUrl($this->url), 'active' => true];
|
|
return Menu::widget([
|
|
'options' => ['class' => 'list-unstyled', 'style' => 'margin-bottom:0;'],
|
|
'items' => $items,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getReadmeHtml()
|
|
{
|
|
if ($this->readme) {
|
|
if ($this->readme_format == 'markdown') {
|
|
$parser = new GithubMarkdown();
|
|
return $parser->parse($this->readme);
|
|
}
|
|
if ($this->readme_format == 'text') {
|
|
return '<pre>' . $this->readme . '</pre>';
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getBowerJson()
|
|
{
|
|
if ($this->bower) {
|
|
$bower = json_encode($this->bower, JSON_PRETTY_PRINT);
|
|
} else {
|
|
$bower = json_encode([
|
|
'name' => $this->name,
|
|
'description' => $this->description ? $this->description : 'Description of your mod.',
|
|
'keywords' => [$this->name],
|
|
'homepage' => $this->homepage ? $this->homepage : Git::getUrl($this->url),
|
|
'screenshots' => ['https://example.com/screenshot1.png'],
|
|
'authors' => $this->authors ? $this->authors : ['Your Name'],
|
|
'license' => 'UNKNOWN',
|
|
], JSON_PRETTY_PRINT);
|
|
}
|
|
$bower = str_replace('\/', '/', $bower);
|
|
return '<pre>' . $bower . '</pre>';
|
|
}
|
|
}
|