blob: c394793a836e2376e14c5e820db295dd1accf910 [file] [log] [blame]
<?php
/*******************************************************************************
* Copyright (c) 2016, 2018 Eclipse Foundation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
require_once dirname(__FILE__) . '/../classes/database.inc';
require_once dirname(__FILE__) . '/../classes/common.php';
require_once dirname(__FILE__) . '/../classes/debug.php';
require_once dirname(__FILE__) . '/../classes/Project.class.php';
class CQ {
var $data;
var $name;
var $version;
function __construct($data) {
$this->data = $data;
$summary = $data[0]['summary'];
if (preg_match('/(.+)\s+version:?\s*v?(\d+(?:\.\d+)+)/i', $summary, $matches)) {
$this->name = $matches[1];
$this->version = $matches[2];
} else if (preg_match('/(.+)\s+v?(\d+(?:\.\d+)+)/i', $summary, $matches)) {
$this->name = $matches[1];
$this->version = $matches[2];
} else {
$this->name = preg_replace('/\([^\)]*\)/', '', $summary);
if (preg_match('/(\d+(?:\.\d+)+)/i', $summary, $matches)) {
$this->version = $matches[0];
} else {
$this->version = 'n/a';
}
}
}
function getId() {
return $this->data[0]['id'];
}
function getSummary() {
return $this->data[0]['summary'];
}
function getName() {
return $this->name;
}
function getVersion() {
return $this->version;
}
function getLicense() {
return $this->data[0]['license'];
}
private function getComment($index = 0) {
return $this->data[0]['comment'];
}
function getProjectUrl() {
$comment = $this->getComment(0);
if (preg_match('/^Project URL:\s*(\S+)$/m', $comment, $matches)) {
return trim($matches[1]);
}
if ($parent = $this->getParent())
return $parent->getProjectUrl();
}
function getSourceUrl() {
$comment = $this->getComment(0);
if (preg_match('/^Source URL:\s*(\S+)$/m', $comment, $matches)) {
return trim($matches[1]);
}
if ($parent = $this->getParent())
return $parent->getSourceUrl();
}
function getRoot() {
if ($parent = $this->getParent())
return $parent->getRoot();
return $this;
}
function getParent() {
if (@$this->parent === null) {
$this->parent = $this->findParent();
}
return $this->parent;
}
private function findParent() {
if (!$id = $this->getParentId()) return FALSE;
$sql = "
select
b.bug_id as id, b.short_desc as summary, b.cf_license as license, l.thetext as comment
from bugs as b
join longdescs as l on b.bug_id=l.bug_id
where
b.bug_id=$id
order by b.bug_id, l.comment_id";
if ($cqs = self::findCqs($sql)) return $cqs[0];
return FALSE;
}
public function getParentId() {
$summary = $this->getSummary();
if (preg_match('/ATO\s+Orbit\s+(\d+)/', $summary, $matches))
return $matches[1];
if (preg_match('/ATO\s*CQ\s*([0-9]+)/', $summary, $matches))
return $matches[1];
if (preg_match('/PB\s*CQ\s*([0-9]+)/', $summary, $matches))
return $matches[1];
if (preg_match('/PB\s*([0-9]+)/', $summary, $matches))
return $matches[1];
if (preg_match('/Orbit\s*(?:CQ)?\s*([0-9]+)/', $summary, $matches))
return $matches[1];
return null;
}
public function __toString() {
return "{$this->getName()} ({$this->getVersion()})";
}
/**
* Answers the approved third party CQs for a project.
*
* @param string $id A project id, e.g. 'technology.egit'
* @return CQ[]
*/
static function findCqs($id) {
$sql = "
select
b.bug_id as id, b.short_desc as summary, b.cf_license as license, l.thetext as comment
from bugs as b
join longdescs as l on b.bug_id=l.bug_id
join components as c on b.component_id=c.id
join keywords as kw on b.bug_id=kw.bug_id
join keyworddefs as kwd on kw.keywordid=kwd.id
where
b.bug_severity in ('approved', 'license_certified')
and kwd.name='thirdparty'
and c.name='$id'
order by b.short_desc";
$data = array();
query('ipzilla', $sql, array(), function($row) use (&$data) {
$id = $row['id'];
$data[$id][] = $row;
});
$cqs = array();
foreach($data as $id => $rows) {
$cqs[] = new CQ($rows);
}
usort($cqs, function($a, $b) {
if (($compare = strcasecmp($a->getName(), $b->getName())) !== 0) {
return $compare;
}
return version_compare($a->getVersion(), $b->getVersion());
});
return $cqs;
}
}
/**
* Execute a function with the CQ for each piece of third
* party content leveraged by the project. The caller can
* provide an optional function to execute in the event that
* the project has no third party CQs.
*
* @param string $id The project id
* @param callable $function Function to execute once with each CQ.
* @param callable $else Function to execute if there are no CQs.
*/
function thirdPartyLibrariesDo($id, $function, $else = null) {
$cqs = CQ::findCqs ($id);
if ($cqs) foreach ($cqs as $cq ) call_user_func($function,$cq);
else if ($else) call_user_func($else);
}
function getDefaultFileHeader($licenses, $width=80) {
return renderAsHeaderComment(License::getDefaultFileHeader($licenses), $width);
}
function getAlternativeFileHeader($licenses, $width=80) {
return renderAsHeaderComment(License::getAlternativeFileHeader($licenses), $width);
}
function renderNoticeFile($id, $width=80) {
generateNoticeFileContent($id, new PlainTextRender($width));
}
function generateNoticeFileContent($id, $render) {
$project = Project::getProject($id);
$render
->title("Notices for " . $project->getFormalName())
->paragraph(
"This content is produced and maintained by the
{$project->getFormalName()} project.")
->unorderedList(array("Project home: {$project->getUrl()}"))
->section("Trademarks")
->paragraph(getTrademarksStatement($project))
->section("Copyright")
->paragraph("All content is the property of the respective authors "
."or their employers. For more information regarding authorship "
."of content, please consult the listed source code repository logs.")
->section("Declared Project Licenses")
->paragraph(License::getLicensesStatement($project->getLicenses()))
->paragraph("SPDX-License-Identifier: " . $project->getSPDXLicenseExpression())
->section("Source Code")
->paragraph("The project maintains the following source code repositories:")
->unorderedList($project->getSourceRepositories(), function($repository) {return $repository->getUrl();});
$render
->section("Third-party Content")
->paragraph("This project leverages the following third party content.");
thirdPartyLibrariesDo($id, function($cq) use (&$render) {
$properties = array();
if ($cq->getLicense()) $properties[] = "License: {$cq->getLicense()}";
if ($cq->getProjectUrl()) $properties[] = "Project: {$cq->getProjectUrl()}";
if ($cq->getSourceUrl()) $properties[] = "Source: {$cq->getSourceUrl()}";
$render
->paragraph("{$cq->getName()} ({$cq->getVersion()})", 3)
->unorderedList($properties);
}, function() use (&$render) {
$render->paragraph("None");
});
$render
->section("Cryptography")
->paragraph(
"Content may contain encryption software.
The country in which you are currently may have restrictions on
the import, possession, and use, and/or re-export to another country,
of encryption software. BEFORE using any encryption software, please
check the country's laws, regulations and policies concerning the import,
possession, or use, and re-export of encryption software, to see if
this is permitted.")
->end();
}
function getTrademarksStatement(Project $project) {
$trademarks = array();
if ($unregistered = $project->getTrademarks(TRADEMARK_UNREGISTERED, true)) {
$content = implodeWithConjunction($unregistered, 'and');
$content .= count($unregistered) == 1 ? ' is a trademark' : ' are trademarks';
$content .= ' of the Eclipse Foundation.';
$trademarks[] = $content;
}
if ($registered = $project->getTrademarks(TRADEMARK_REGISTERED, true)) {
$content = implodeWithConjunction($registered, 'and');
$content .= count($registered) == 1 ? ' is a registered trademark' : ' are registered trademarks';
$content .= ' of the Eclipse Foundation.';
$trademarks[] = $content;
}
if ($trademarks) {
return join(' ', $trademarks);
}
return "No trademarks.";
}
function renderContributingFile($id, $width=80) {
generateContributingFileContent($id, new PlainTextRender($width));
}
function generateContributingFileContent($id, $render) {
$project = Project::getProject($id);
$forge = $project->getForge();
$render
->title("Contributing to " . $project->getFormalName())
->paragraph("Thanks for your interest in this project.")
->section("Project description")
->paragraph(trim(html_entity_decode(strip_tags($project->getDescription()))))
->unorderedList(array($project->getUrl()))
->section("Developer resources")
->paragraph("Information regarding source code management, builds, coding standards, and more.")
->unorderedList(array($project->getUrl() . "/developer"))
->paragraph("The project maintains the following source code repositories")
->unorderedList($project->getSourceRepositories(),
function($each) {
return $each->getUrl();
});
if ($product = $project->getBugzillaProduct()) {
if ($forge->isEclipseForge()) {
$base = 'https://bugs.eclipse.org/bugs';
} else {
$base = "https://{$forge->getId()}.org/bugs";
}
$search = "$base/buglist.cgi?product=$product";
$create = "$base/enter_bug.cgi?product=$product";
if ($components = $project->getBugzillaComponents()) {
$search .= "&" . $components[1];
$create .= "&" . $components[1];
}
$render
->paragraph("This project uses Bugzilla to track ongoing development and issues.")
->unorderedList(
array(
"Search for issues: {$search}",
"Create a new report: {$create}")
)
->paragraph("Be sure to search for existing bugs before you create another one."
." Remember that contributions are always welcome!");
}
$render
->section("Eclipse Contributor Agreement")
->paragraph(
"Before your contribution can be accepted by the project team
contributors must electronically sign the
Eclipse Contributor Agreement (ECA).")
->unorderedList(array("http://www.eclipse.org/legal/ECA.php"))
->paragraph(
"Commits that are provided by non-committers must have a
Signed-off-by field in the footer indicating that the author
is aware of the terms by which the contribution has been
provided to the project. The non-committer must additionally
have an Eclipse Foundation account and must have a signed Eclipse
Contributor Agreement (ECA) on file.")
->paragraph(
"For more information, please see the Eclipse Committer Handbook:
https://www.eclipse.org/projects/handbook/#resources-commit")
->section("Contact")
->paragraph("Contact the project developers via the project's \"dev\" list.")
->unorderedList(array($project->getDevListUrl()))
->end();
}
/**
* A very simple class for rendering structured content
* in plaintext.
*/
class PlainTextRender {
var $width;
function __construct($width) {
$this->width = $width;
}
function title($text) {
$this->section($text, 1);
return $this;
}
function section($text, $level=2) {
for($index=0;$index<$level;$index++) echo "#";
echo " ";
echo $text;
echo "\n\n";
return $this;
}
function paragraph($text) {
echo wordwrap($this->normalize($text), $this->width);
echo "\n\n";
return $this;
}
private function normalize($text) {
return preg_replace('/\s+/', ' ', $text);
}
function unorderedList($values, $each = 'doNothing') {
foreach($values as $value) {
echo "* ";
$lines = explode("\n",wordwrap($each($value), $this->width-3));
echo implode("\n ", $lines);
echo "\n";
}
echo "\n";
return $this;
}
function end() {}
}
function doNothing($value) {
return $value;
}