Refactoring.

Move License class to a separate file. Move license behavior from
Project to License class. General refactoring.
diff --git a/classes/License.class.inc b/classes/License.class.inc
new file mode 100644
index 0000000..aa0f55c
--- /dev/null
+++ b/classes/License.class.inc
@@ -0,0 +1,203 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2017 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__) . "/database.inc");
+
+class License {
+ var $id;
+ var $text;
+ var $url;
+
+ private function __construct($id, $text, $url) {
+  $this->id = $id;
+  $this->text = $text;
+  $this->url = $url;
+ }
+
+ public static function getLicense($id) {
+  // FIXME This should be in the (a) database.
+  switch ($id) {
+   case 'EPL2.0':
+   case 'EPL-2.0':
+    return new License('EPL-2.0', 'Eclipse Public License v. 2.0', 'http://www.eclipse.org/legal/epl-2.0');
+   case 'EPL1.0':
+   case 'EPL-1.0':
+    return new License('EPL-1.0', 'Eclipse Public License v. 1.0', 'http://www.eclipse.org/legal/epl-v10.html');
+   case 'EDL1.0':
+   case 'EDL-1.0':
+   case 'EDL':
+    return new License('LicenseRef-EDL-1.0', 'Eclipse Distribution License v. 1.0', 'http://www.eclipse.org/org/documents/edl-v10.php');
+   case 'ASL2.0':
+   case 'Apache-2.0':
+    return new License('Apache-2.0', 'Apache License, Version 2.0', 'https://www.apache.org/licenses/LICENSE-2.0');
+   case 'CCBY3':
+   case 'CC-BY-3.0':
+    return new License('CC-BY-3.0', 'Creative Commons Attribution 3.0 Unported', 'https://creativecommons.org/licenses/by/4.0/');
+   case 'GPL-2.0':
+    return new License('GPL-2.0', 'GNU General Public License, version 2', 'https://www.gnu.org/copyleft/gpl.html');
+   case 'GPL-2.0_CP':
+    return new License('GPL-2.0 WITH Classpath-exception-2.0', 'GNU General Public License, version 2 with the GNU Classpath Exception', 'https://www.gnu.org/software/classpath/license.html');
+   case 'GPL-2.0_AE':
+    return new License('LicenseRef-GPL-2.0-with-Assembly-exception', 'GNU General Public License, version 2 with OpenJDK Assembly Exception', 'http://openjdk.java.net/legal/assembly-exception.html');
+
+   default:
+    return null;
+  }
+ }
+
+ public static function getLicenses($codes) {
+  $licenses = array();
+  foreach($codes as $code) {
+   $licenses[] = self::getLicense($code);
+  }
+  return $licenses;
+ }
+
+ /**
+  * Return an array of licenses for a project.
+  *
+  * @param string $id Project Id
+  * @return License[]
+  */
+ public static function getLicensesForProject($id) {
+  $sql = "
+    select
+       l.LicenseId as id,
+       l.description as text
+    from ProjectLicenses as pl
+      join SYS_Licenses as l on pl.LicenseId=l.LicenseId
+    where pl.ProjectId='$id'";
+
+  $licenses = array ();
+  query ( 'foundation', $sql, array (), function ($row) use (&$licenses) {
+   $licenses [] = self::getLicense($row ['id']);
+  } );
+
+   // Make sure that the EPL is always at the top of the list.
+   usort($licenses, function($a, $b) {
+    if ($a->getId() == 'EPL-2.0') return -1;
+    if ($b->getId() == 'EPL-2.0') return 1;
+    if ($a->getId() == 'EPL-1.0') return -1;
+    if ($b->getId() == 'EPL-1.0') return 1;
+    return strcasecmp($a->getId(), $b->getId());
+   });
+    return $licenses;
+ }
+
+
+ public static function getDefaultFileHeader($licenses) {
+  $lines = array();
+  $lines[] = "Copyright (c) {date} {owner}[ and others]";
+  $lines[] = self::getLicensesStatement($licenses);
+  $lines[] = "SPDX-License-Identifier: " . self::getSPDXExpression($licenses);
+  return implode("\n\n", $lines);
+ }
+
+ public static function getAlternativeFileHeader($licenses) {
+  $lines = array();
+  $lines[] = "Copyright (c) {date} Contributors to the Eclipse Foundation";
+  $lines[] = "See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership.";
+  $lines[] = self::getLicensesStatement($licenses);
+  $lines[] = "SPDX-License-Identifier: " . self::getSPDXExpression($licenses);
+  return implode("\n\n", $lines);
+ }
+
+ /**
+  * Answer a statement (suitable for use in a file header)
+  * that describes the licenses of the project.
+  *
+  * @param $licenses License[]
+  * @param $nbsp string value to use as the "non-breaking" space in the license names.
+  * @return string
+  */
+ public static function getLicensesStatement($licenses) {
+  // Initial statement of license(s)
+  $parts = array();
+  $secondary = array();
+  foreach($licenses as $license) {
+   $text = $license->getText();
+   if ($license->isSecondaryLicense()) {
+    $secondary[] = "{$text} which is available at {$license->getUrl()}";
+   } else {
+    $parts[] = "{$text} which is available at {$license->getUrl()}";
+   }
+  }
+
+  $statement = "This program and the accompanying materials are made available under the terms of the ";
+
+  $statement .= implodeWithConjunction($parts, 'or the') . ".";
+
+  if ($secondary) {
+   $statement .= "\n\n";
+   // FIXME License text is inlined; should come from data.
+   $epl20 = License::getLicense('EPL-2.0');
+   // FIXME We assume that secondary licenses only apply to EPL-2.0 (true for now)
+   $statement .= "This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the {$epl20->getText()} are satisfied: ";
+   $statement .= implodeWithConjunction($secondary, 'and') . ".";
+  }
+
+  return $statement;
+ }
+
+ public static function getSPDXExpression($licenses) {
+  $codes = array();
+
+  foreach($licenses as $license) {
+    $codes[] = $license->getSPDXCode();
+  }
+
+  return implode(' OR ', $codes);
+ }
+
+ public static function parseLicenses($text) {
+  $licenses = array();
+  foreach(explode(',', $text) as $code) {
+   if ($license = self::getLicense($code)) {
+    $licenses[] = $license;
+   }
+  }
+  if (!$licenses) $licenses[] = License::getLicense('EPL-2.0');
+  return $licenses;
+ }
+
+ public function getId() {
+  return $this->id;
+ }
+
+ public function getText() {
+  return $this->text;
+ }
+
+ public function getSPDXCode() {
+  return $this->id;
+ }
+
+ /**
+  * Answers true when the receiver is a secondary
+  * license according to the terms of the EPL-2.0,
+  * or false otherwise.
+  *
+  * Note that the ability for a license to decide
+  * whether or not it is secondary is temporary
+  * functionality based on a stop-gap implementation.
+  *
+  * TODO Reconsider this implementation.
+  */
+ public function isSecondaryLicense() {
+  return preg_match("/^GPL/", $this->getId());
+ }
+
+ public function getUrl() {
+  return $this->url;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/classes/Project.class.php b/classes/Project.class.php
index 7473e34..7c9d997 100644
--- a/classes/Project.class.php
+++ b/classes/Project.class.php
@@ -1,19 +1,20 @@
 <?php
-/**
- * *****************************************************************************
- * Copyright (c) 2010, 2016 Eclipse Foundation and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- * *****************************************************************************
- */
+/*******************************************************************************
+ * Copyright (c) 2010, 2017 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
+ ********************************************************************************/
 
 /*
  * This script assumes that it is being included by another script. We
  * assume that the $App variable has already been defined.
  */
 require_once (dirname(__FILE__) . "/Forge.class.inc");
+require_once (dirname(__FILE__) . "/License.class.inc");
 require_once (dirname(__FILE__) . "/ProjectRoot.class.php");
 require_once (dirname(__FILE__) . "/database.inc");
 require_once (dirname(__FILE__) . "/debug.php");
@@ -571,88 +572,48 @@
     return $this->data['phase'];
   }
 
+  /**
+   *
+   * @deprecated
+   * @return License[]
+   */
   public function getLicenses() {
-   $id = $this->getId();
-   $sql = "
-    SELECT
-      if(l.LicenseId='EPL1.0','EPL-1.0',
-        if(l.LicenseId='EPL2.0','EPL-2.0',
-         if(l.LicenseId='EDL1.0','EDL-1.0',
-          if(l.LicenseId='ASL2.0','Apache-2.0',
-           if(l.LicenseId='CCBY3','CC-BY-3.0',
-            l.LicenseId
-           )
-          )
-         )
-        )
-       ) as id,
-       l.description as text
-     FROM ProjectLicenses as pl
-      join SYS_Licenses as l on pl.LicenseId=l.LicenseId
-     where pl.ProjectId='$id'";
-
-   $licenses = array ();
-   query ( 'foundation', $sql, array (), function ($row) use (&$licenses) {
-    $licenses [] = new License($row ['id'], $row ['text']);
-   } );
-
-   // Make sure that the EPL is always at the top of the list.
-   usort($licenses, function($a, $b) {
-    if ($a->getId() == 'EPL-2.0') return -1;
-    if ($b->getId() == 'EPL-2.0') return 1;
-    if ($a->getId() == 'EPL-1.0') return -1;
-    if ($b->getId() == 'EPL-1.0') return 1;
-    return strcasecmp($a->getId(), $b->getId());
-   });
-   return $licenses;
+   return License::getLicensesForProject($this->getId());
   }
 
+  /**
+   * Answer a statement (suitable for use in a file header)
+   * that describes the licenses of the project.
+   *
+   * @deprecated Use License::getLicensesStatement
+   * @return string
+   */
   public function getLicensesStatement() {
-   // Initial statement of license(s)
-   $parts = array();
-   $secondary = array();
-   foreach($this->getLicenses() as $license) {
-    if ($license->isSecondaryLicense()) {
-     $secondary[] = "{$license->getText()} which is available at {$license->getUrl()}";
-    } else {
-     $parts[] = "{$license->getText()} which is available at {$license->getUrl()}";
-    }
-   }
-
-   $statement = "This program and the accompanying materials are made available under the terms of the ";
-
-   $statement .= implodeWithConjunction($parts, 'or the') . ".";
-
-   if ($secondary) {
-    $statement .= "\n\n";
-    // FIXME License text is inlined; should come from data.
-    // FIXME We assume that secondary licenses only apply to EPL-2.0 (true for now)
-    $statement .= "This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License v. 2.0 are satisfied: ";
-    $statement .= implodeWithConjunction($secondary, 'and') . ".";
-   }
-
-   return $statement;
+   return License::getLicensesStatement($this->getLicenses());
   }
 
+  /**
+   * @deprecated Use License::getSPDXLicenseExpression
+   * @return string
+   */
   public function getSPDXLicenseExpression() {
-   return License::buildSPDXExpression($this->getLicenses());
+   return License::getSPDXExpression($this->getLicenses());
   }
 
+  /**
+   * @deprecated Use License::getDefaultFileHeader
+   * @return string
+   */
   public function getDefaultFileHeader() {
-   $lines = array();
-   $lines[] = "Copyright (c) {date} {owner}[ and others]";
-   $lines[] = $this->getLicensesStatement();
-   $lines[] = "SPDX-License-Identifier: " . $this->getSPDXLicenseExpression();
-   return implode("\n\n", $lines);
+   return License::getDefaultFileHeader($this->getLicenses());
   }
 
+  /**
+   * @deprecated Use License::getAlternativeFileHeader
+   * @return string
+   */
   public function getAlternativeFileHeader() {
-   $lines = array();
-   $lines[] = "Copyright (c) {date} Contributors to the Eclipse Foundation";
-   $lines[] = "See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership.";
-   $lines[] = $this->getLicensesStatement();
-   $lines[] = "SPDX-License-Identifier: " . $this->getSPDXLicenseExpression();
-   return implode("\n\n", $lines);
+   return License::getAlternativeFileHeader($this->getLicenses());
   }
 
   /**
@@ -1193,87 +1154,4 @@
   return strcasecmp($a->getTopLevelProject()->getName(), $b->getTopLevelProject()->getName());
 }
 
-class License {
- var $id;
- var $text;
-
- public function __construct($id, $text) {
-  $this->id = $id;
-  $this->text = $text;
- }
-
- public static function buildSPDXExpression($licenses) {
-  $codes = array();
-  $secondary = array();
-  foreach($licenses as $license) {
-   if ($license->isSecondaryLicense()) {
-    $secondary[] = $license->getSPDXCode();
-   }
-  }
-
-  foreach($licenses as $license) {
-   if ($license->isSecondaryLicense()) continue;
-   if ($license->getId() == 'EPL-2.0' && $secondary) {
-    $clause = count($secondary) == 1 ? $secondary[0] : ('(' . implode(' OR ', $secondary) . ')');
-    $codes[] = '(' . $license->getSPDXCode() . ' OR ' . $clause . ')';
-   }else {
-    $codes[] = $license->getSPDXCode();
-   }
-  }
-
-  return implode(' OR ', $codes);
- }
-
- public function getId() {
-  return $this->id;
- }
-
- public function getText() {
-  switch ($this->id) {
-   case 'GPL-2.0' : return 'GNU General Public License, version 2';
-   case 'GPL-2.0_CP' : return 'GNU General Public License, version 2 with the GNU Classpath Exception';
-   case 'GPL-2.0_AE' : return 'GNU General Public License, version 2 with OpenJDK Assembly Exception';
-   default : return $this->text;
-  }
- }
-
- public function getSPDXCode() {
-  switch ($this->id) {
-   case 'GPL-2.0' : return 'GPL-2.0';
-   case 'GPL-2.0_CP' : return 'GPL-2.0 WITH Classpath-exception-2.0';
-   case 'GPL-2.0_AE' : return 'LicenseRef-GPL-2.0-with-Assembly-exception';
-   default : return $this->id;
-  }
- }
-
- /**
-  * Answers true when the receiver is a secondary
-  * license according to the terms of the EPL-2.0,
-  * or false otherwise.
-  *
-  * Note that the ability for a license to decide
-  * whether or not it is secondary is temporary
-  * functionality based on a stop-gap implementation.
-  *
-  * TODO Reconsider this implementation.
-  */
- public function isSecondaryLicense() {
-  return preg_match("/^GPL/", $this->getId());
- }
-
- public function getUrl() {
-  // FIXME Put the URLs in the Database.
-  switch ($this->getId()) {
-   case 'EPL-1.0' : return 'http://www.eclipse.org/legal/epl-v10.html';
-   case 'EPL-2.0' : return 'http://www.eclipse.org/legal/epl-2.0';
-   case 'Apache-2.0' : return 'https://www.apache.org/licenses/LICENSE-2.0';
-   case 'EDL-1.0' : return 'http://www.eclipse.org/org/documents/edl-v10.php';
-   case 'GPL-2.0' : return 'https://www.gnu.org/copyleft/gpl.html';
-   case 'GPL-2.0_CP' : return 'https://www.gnu.org/software/classpath/license.html';
-   case 'GPL-2.0_AE' : return 'http://openjdk.java.net/legal/assembly-exception.html';
-   case 'CC-BY-4.0' : return 'https://creativecommons.org/licenses/by/4.0/';
-   default : return 'http://www.eclipse.org/legal';
-  }
- }
-}
 ?>
\ No newline at end of file
diff --git a/classes/common.php b/classes/common.php
index 0c350f1..13fd4bb 100644
--- a/classes/common.php
+++ b/classes/common.php
@@ -1,16 +1,14 @@
 <?php
-/**
- * *****************************************************************************
- * Copyright (c) 2010, 2015 Eclipse Foundation and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
+/*******************************************************************************
+ * Copyright (c) 2010, 2017 Eclipse Foundation and others.
  *
- * Contributors:
- * Wayne Beaton (Eclipse Foundation)- initial API and implementation
- * *****************************************************************************
- */
+ * 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
+ ********************************************************************************/
+
 
 // TODO Merge with web-api/common.inc
 require_once (dirname(__FILE__) . "/debug.php");
diff --git a/tools/about.php b/tools/about.php
index 9942d98..ebf7c0f 100644
--- a/tools/about.php
+++ b/tools/about.php
@@ -1,14 +1,14 @@
 <?php
 /*******************************************************************************
- * Copyright (c) 2016 Eclipse Foundation and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
+ * Copyright (c) 2016, 2017 Eclipse Foundation and others.
  *
- * Contributors:
- *    Wayne Beaton (Eclipse Foundation)- initial API and implementation
- *******************************************************************************/
+ * 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__) . "/../../eclipse.org-common/system/app.class.php");
 require_once(dirname(__FILE__) . "/../../eclipse.org-common/system/nav.class.php");
@@ -24,10 +24,12 @@
 $pageKeywords	= "";
 $pageAuthor		= "Wayne Beaton";
 
-$id = $_GET['id'];
+if (!$id = @$_GET['id']) exit();
 if (!isValidProjectId($id)) exit();
 if (!$project = Project::getProject($id)) exit();
 
+if (!$width = (int)@$_GET['width']) $width = 80;
+
 ob_start();
 
 function renderTemplate($function) {
@@ -36,74 +38,6 @@
  echo "</pre>";
 }
 
-function thirdPartyLibrariesDo($id, $function) {
- foreach ( CQ::findCqs ($id) as $cq ) {
-  $function($cq);
- }
-}
-
-function renderNoticeFile($id, $width=80) {
- renderTemplate(function() use ($id, $width) {
-  $project = Project::getProject($id);
-
-  echo wordwrap("This content is produced and maintained by the {$project->getFormalName()} project.", $width);
-  echo "\n\n";
-  echo "* Project home: {$project->getUrl()}\n";
-
-  $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) {
-   echo "\n";
-   echo wordwrap(join(' ', $trademarks), $width);
-   echo "\n";
-  }
-
-  echo "\n== Declared Project Licenses\n\n";
-  echo wordwrap($project->getLicensesStatement(), $width);
-  echo "\n\n";
-  echo wordwrap("SPDX-License-Identifier: " . $project->getSPDXLicenseExpression(), $width);
-  echo "\n";
-
-  if ($repositories = $project->getSourceRepositories()) {
-   echo "\n== Source Code\n\n";
-   echo "The project maintains the following source code repositories:\n\n";
-   foreach($repositories as $repository) {
-    echo "* {$repository->getUrl()}\n";
-   }
-  }
-
-  echo "\n== Copyright Holders\n\n";
-  copyrightStatementsDo ( $id, function ($statement) {
-   echo "$statement\n";
-  } );
-
-  echo "\n== Third-party Content\n";
-  thirdPartyLibrariesDo($id, function($cq) {
-   echo "\n{$cq->getName()}\n";
-   if ($cq->getLicense()) echo "* License: {$cq->getLicense()}\n";
-   if ($cq->getProjectUrl()) echo "* Project: {$cq->getProjectUrl()}\n";
-   if ($cq->getSourceUrl()) echo "* Source: {$cq->getSourceUrl()}\n";
-  });
-
-  echo "\n== Cryptography\n\n";
-  $crypto = "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.";
-  echo wordwrap($crypto, $width);
-  echo "\n";
-
- });
-}
-
 ?>
 <div id="maincontent">
 	<div id="midcolumn">
@@ -132,8 +66,8 @@
 		</p>
 
 		<?php
-     renderTemplate ( function () use (&$project) {
-      echo renderAsHeaderComment ( $project->getDefaultFileHeader () );
+     renderTemplate ( function () use (&$project, $width) {
+      echo getDefaultFileHeader($project->getLicenses(), $width);
      } );
     ?>
 
@@ -142,8 +76,8 @@
 			the copyright holders in the notice file.</p>
 
 		<?php
-     renderTemplate ( function () use (&$project) {
-      echo renderAsHeaderComment ( $project->getAlternativeFileHeader () );
+		renderTemplate ( function () use (&$project, $width) {
+		 echo getAlternativeFileHeader($project->getLicenses(), $width);
      } );
     ?>
 
@@ -158,7 +92,7 @@
 		</p>
 
 		<?php
-     renderNoticeFile($id);
+     renderNoticeFile($id, $width);
     ?>
 
 </div>
diff --git a/tools/legal.inc b/tools/legal.inc
index b429be3..20e5514 100644
--- a/tools/legal.inc
+++ b/tools/legal.inc
@@ -1,21 +1,42 @@
 <?php
 /*******************************************************************************
  * Copyright (c) 2016, 2017 Eclipse Foundation and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
+ *
+ * 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() {
@@ -27,9 +48,11 @@
     }
 
     function getName() {
-        $value = $this->getSummary();
-        $value = preg_replace('/\([^\)]*\)/', '', $value);
-        return $value;
+     return $this->name;
+    }
+
+    function getVersion() {
+     return $this->version;
     }
 
     function getLicense() {
@@ -105,6 +128,9 @@
         return null;
     }
 
+    public function __toString() {
+     return "{$this->getName()} ({$this->getVersion()})";
+    }
     /**
      * Answers the approved third party CQs for a project.
      *
@@ -124,7 +150,7 @@
              b.bug_severity='approved'
              and kwd.name='thirdparty'
              and c.name='$id'
-         order by b.bug_id, l.comment_id";
+         order by b.short_desc";
 
         $data = array();
         query('ipzilla', $sql, array(), function($row) use (&$data) {
@@ -137,10 +163,92 @@
             $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;
     }
 }
 
+function thirdPartyLibrariesDo($id, $function) {
+ foreach ( CQ::findCqs ($id) as $cq ) {
+  $function($cq);
+ }
+}
+
+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) {
+ renderTemplate(function() use ($id, $width) {
+  $project = Project::getProject($id);
+
+  echo wordwrap("This content is produced and maintained by the {$project->getFormalName()} project.", $width);
+  echo "\n\n";
+  echo "* Project home: {$project->getUrl()}\n";
+
+  $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) {
+   echo "\n";
+   echo wordwrap(join(' ', $trademarks), $width);
+   echo "\n";
+  }
+
+  echo "\n== Declared Project Licenses\n\n";
+  echo wordwrap(License::getLicensesStatement($project->getLicenses()), $width);
+  echo "\n\n";
+  echo wordwrap("SPDX-License-Identifier: " . $project->getSPDXLicenseExpression(), $width);
+  echo "\n";
+
+  if ($repositories = $project->getSourceRepositories()) {
+   echo "\n== Source Code\n\n";
+   echo "The project maintains the following source code repositories:\n\n";
+   foreach($repositories as $repository) {
+    echo "* {$repository->getUrl()}\n";
+   }
+  }
+
+  echo "\n== Copyright Holders\n\n";
+  copyrightStatementsDo ( $id, function ($statement) {
+   echo "$statement\n";
+  } );
+
+   echo "\n== Third-party Content\n";
+   thirdPartyLibrariesDo($id, function($cq) {
+    echo "\n{$cq->getName()} ({$cq->getVersion()})\n";
+    if ($cq->getLicense()) echo "* License: {$cq->getLicense()}\n";
+    if ($cq->getProjectUrl()) echo "* Project: {$cq->getProjectUrl()}\n";
+    if ($cq->getSourceUrl()) echo "* Source: {$cq->getSourceUrl()}\n";
+   });
+
+    echo "\n== Cryptography\n\n";
+    $crypto = "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.";
+    echo wordwrap($crypto, $width);
+    echo "\n";
+ });
+}
+
 function copyrightStatementsDo($id, $function) {
  $sql = "
   select distinct
@@ -153,7 +261,8 @@
   from
    ProjectCopyrights
   where
-   project=':project'";
+   project=':project'
+  order by first";
 
  query('dashboard', $sql, array(':project' => $id), function($row) use (&$function) {
   $function($row['statement']);
diff --git a/tools/legal/header.php b/tools/legal/header.php
new file mode 100644
index 0000000..59d719a
--- /dev/null
+++ b/tools/legal/header.php
@@ -0,0 +1,22 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2017 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
+ ********************************************************************************/
+
+header("Content-Type: text/plain");
+
+require_once dirname(__FILE__) . '/../legal.inc';
+require_once dirname(__FILE__) . '/../../classes/License.class.inc';
+require_once dirname(__FILE__) . '/../../classes/common.php';
+require_once dirname(__FILE__) . '/../../classes/debug.php';
+
+if (!$width = (int)@$_GET['width']) $width = 80;
+$licenses = License::parseLicenses(@$_GET['id']);
+
+echo getDefaultFileHeader($licenses, $width);
\ No newline at end of file