Skip to content

Instantly share code, notes, and snippets.

@krusynth
Created December 30, 2013 00:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save krusynth/8176428 to your computer and use it in GitHub Desktop.
Save krusynth/8176428 to your computer and use it in GitHub Desktop.
Possible fix for linking to non-numbered sections.
<?php
/**
* The Law class, for retrieving data about individual laws.
*
* PHP version 5
*
* @license http://www.gnu.org/licenses/gpl.html GPL 3
* @version 0.8
* @link http://www.statedecoded.com/
* @since 0.1
*
*/
class Law
{
/**
* Retrieve all of the material relevant to a given law.
*/
function get_law()
{
/*
* We're going to need access to the database connection throughout this class.
*/
global $db;
/*
* If neither a section number nor a law ID has been passed to this function, then there's
* nothing to do.
*/
if (!isset($this->section_number) && !isset($this->law_id))
{
return FALSE;
}
/*
* If we haven't specified which fields that we want, then assume that we want all of them.
*/
if (!isset($this->config) || !is_object($this->config) )
{
$this->config->get_all = TRUE;
}
/*
* Define the level of detail that we want from this method. By default, we return
* everything that we have for this law.
*/
if ( !isset($this->config) || ($this->config->get_all == TRUE) )
{
$this->config->get_text = TRUE;
$this->config->get_structure = TRUE;
$this->config->get_amendment_attempts = TRUE;
$this->config->get_court_decisions = TRUE;
$this->config->get_metadata = TRUE;
$this->config->get_references = TRUE;
$this->config->get_related_laws = TRUE;
$this->config->get_tags = TRUE;
$this->config->render_html = TRUE;
}
/*
* Assemble the query that we'll use to get this law.
*/
$sql = 'SELECT id AS section_id, structure_id, section AS section_number, catch_line,
history, text AS full_text, order_by
FROM laws';
$sql_args = array();
/*
* If we're requesting a specific law by ID.
*/
if (isset($this->law_id))
{
/*
* If it's just a single law ID, then just request the one.
*/
if (!is_array($this->law_id))
{
$sql .= ' WHERE id = :id';
$sql_args[':id'] = $this->law_id;
}
/*
* But if it's an array of law IDs, request all of them.
*/
elseif (is_array($this->law_id))
{
$sql .= ' WHERE (';
/*
* Step through the list.
*/
$law_count = count($this->law_id);
for($i = 0; $i < $law_count; $i++)
{
$sql .= " id = :id$i";
$sql_args[":id$i"] = $this->law_id[$i];
if ($i < ($law_count - 1))
{
$sql .= ' OR';
}
}
$sql .= ')';
}
}
/*
* Else if we're requesting a law by section number, then make sure that we're getting the
* law from the newest edition of the laws.
*/
else
{
$sql .= ' WHERE section = :section_number
AND edition_id = :edition_id';
$sql_args[':section_number'] = $this->section_number;
if(isset($this->edition_id))
{
$sql_args[':edition_id'] = $this->edition_id;
}
else
{
$sql_args[':edition_id'] = EDITION_ID;
}
}
$statement = $db->prepare($sql);
$result = $statement->execute($sql_args);
if ( ($result === FALSE) || ($statement->rowCount() == 0) )
{
return FALSE;
}
/*
* Return the result as an object.
*/
$tmp = $statement->fetch(PDO::FETCH_OBJ);
/*
* Bring this law into the object scope.
*/
foreach ($tmp as $key => $value)
{
$this->$key = $value;
}
/*
* Clean up the typography in the full text.
*/
$this->full_text = wptexturize($this->full_text);
/*
* Now get the text for this law, subsection by subsection.
*/
if ($this->config->get_text === TRUE)
{
/*
* When invoking this method in a loop, $this->text can pile up on itself. If the text
* property is already set, clear it out.
*/
if (isset($this->text))
{
unset($this->text);
}
$sql = 'SELECT id, text, type,
(SELECT
GROUP_CONCAT(identifier
ORDER BY sequence ASC
SEPARATOR "|")
FROM text_sections
WHERE text_id=text.id
GROUP BY text_id) AS prefixes
FROM text
WHERE law_id = :law_id
ORDER BY text.sequence ASC';
$sql_args = array(
':law_id' => $this->section_id
);
$statement = $db->prepare($sql);
$result = $statement->execute($sql_args);
/*
* If the query fails, or if no results are found, return false -- we can't make a
* match.
*/
if ( ($result === FALSE) || ($statement->rowCount() == 0) )
{
return FALSE;
}
/*
* Iterate through all of the sections of text to save to our object.
*/
$i=0;
while ($tmp = $statement->fetch(PDO::FETCH_OBJ))
{
$tmp->prefixes = explode('|', $tmp->prefixes);
$tmp->prefix = end($tmp->prefixes);
$tmp->entire_prefix = implode('', $tmp->prefixes);
$tmp->prefix_anchor = str_replace(' ', '_', $tmp->entire_prefix);
$tmp->level = count($tmp->prefixes);
/*
* Pretty it up, converting all straight quotes into directional quotes, double
* dashes into em dashes, etc.
*/
if ($tmp->type != 'table')
{
$tmp->text = wptexturize($tmp->text);
}
/*
* Append this section.
*/
$this->text->$i = $tmp;
$i++;
}
}
/*
* Determine this law's structural position.
*/
if ($this->config->get_structure = TRUE)
{
/*
* Create a new instance of the Structure class.
*/
$struct = new Structure;
/*
* Our structure ID provides a starting point to identify this law's ancestry.
*/
$struct->id = $this->structure_id;
/*
* Save the law's ancestry.
*/
$this->ancestry = $struct->id_ancestry();
/*
* Short of a parser error, there’s no reason why a law should not have an ancestry. In
* case of this unlikely possibility, just erase the false element.
*/
if ($this->ancestry === FALSE)
{
unset($this->ancestry);
}
/*
* Get the listing of all other sections in the structural unit that contains this
* section.
*/
$this->structure_contents = $struct->list_laws();
/*
* Figure out what the next and prior sections are (we may have 0-1 of either). Iterate
* through all of the contents of the chapter. (It's possible that there are no next or
* prior sections, such as in a single-item structural unit.)
*/
if ($this->structure_contents !== FALSE)
{
$tmp = count((array) $this->structure_contents);
for ($i=0; $i<$tmp; $i++)
{
/*
* When we get to our current section, that's when we get to work.
*/
if ($this->structure_contents->$i->id == $this->section_id)
{
$j = $i-1;
$k = $i+1;
if (isset($this->structure_contents->$j))
{
$this->previous_section = $this->structure_contents->$j;
}
if (isset($this->structure_contents->$k))
{
$this->next_section = $this->structure_contents->$k;
}
break;
}
}
}
}
/*
* Gather all metadata stored about this law.
*/
if ($this->config->get_metadata == TRUE)
{
$this->metadata = Law::get_metadata();
}
/*
* Gather any tags applied to this law.
*/
if ($this->config->get_tags == TRUE)
{
$sql = 'SELECT text
FROM tags
WHERE law_id = ' . $db->quote($this->section_id);
$result = $db->query($sql);
if ( ($result !== TRUE) && ($result->rowCount() > 0) )
{
$this->tags = new stdClass();
$i = 0;
while ($tag = $result->fetch(PDO::FETCH_OBJ))
{
$this->tags->{$i} = $tag->text;
$i++;
}
}
}
/*
* Create a new instance of the State() class.
*/
$state = new State();
$state->section_id = $this->section_id;
$state->section_number = $this->section_number;
/*
* Get the amendment attempts for this law and include those (if there are any). But
* only if we have specifically requested this data. That's because, on most installations,
* this will be making a call to a third-party service (e.g., Open States), and such a call
* is expensive.
*/
if ($this->config->get_amendment_attempts == TRUE)
{
if (method_exists($state, 'get_amendment_attempts'))
{
if ($state->get_amendment_attempts() !== FALSE)
{
$this->amendment_attempts = $state->bills;
}
}
}
/*
* Get the amendment attempts for this law and include those (if there are any). But
* only if we have specifically requested this data. That's because, on most installations,
* this will be making a call to a third-party service and such a call is expensive.
*/
if ($this->config->get_court_decisions == TRUE)
{
/*
* If we already have this data cached as metadata.
*/
if (isset($this->metadata->court_decisions))
{
$this->court_decisions = $this->metadata->court_decisions;
}
/*
* If we do not have this data cached.
*/
else
{
if (method_exists($state, 'get_court_decisions'))
{
if ($state->get_court_decisions() !== FALSE)
{
$this->court_decisions = $state->decisions;
}
}
}
}
/*
* Get the URL for this law on its official state web page.
*/
if (method_exists($state, 'official_url'))
{
$this->official_url = $state->official_url();
}
/*
* Translate the history of this law into plain English.
*/
if (method_exists($state, 'translate_history'))
{
if (isset($this->metadata->history))
{
$state->history = $this->metadata->history;
$this->history_text = $state->translate_history();
}
}
/*
* Generate citations for this law.
*/
if (method_exists($state, 'citations'))
{
$state->section_number = $this->section_number;
$state->citations();
$this->citation = $state->citation;
}
/*
* Get the references to this law among other laws and include those (if there are any).
*/
if ($this->config->get_references == TRUE)
{
$this->references = Law::get_references();
}
/*
* Gather all laws that are textually similar to this law.
*/
if ($this->config->get_related_laws == TRUE)
{
//$this->metadata = Law::get_related();
}
/*
* Pretty up the text for the catch line.
*/
$this->catch_line = wptexturize($this->catch_line);
/*
* Provide the URL for this section.
*/
$sql = 'SELECT url, token
FROM permalinks
WHERE relational_id = :id
AND object_type = :object_type';
$statement = $db->prepare($sql);
$sql_args = array(
':id' => $this->section_id,
':object_type' => 'law'
);
$result = $statement->execute($sql_args);
if ( ($result !== FALSE) && ($statement->rowCount() > 0) )
{
$permalink = $statement->fetch(PDO::FETCH_OBJ);
$this->url = $permalink->url;
$this->token = $permalink->token;
}
/*
* List the URLs for the textual formats in which this section is available.
*/
$this->formats->txt = substr($this->url, 0, -1) . '.txt';
$this->formats->json = substr($this->url, 0, -1) . '.json';
$this->formats->json = substr($this->url, 0, -1) . '.xml';
/*
* Create metadata in the Dublin Core format.
*/
$this->dublin_core = new stdClass();
$this->dublin_core->Title = $this->catch_line;
$this->dublin_core->Type = 'Text';
$this->dublin_core->Format = 'text/html';
$this->dublin_core->Identifier = SECTION_SYMBOL . ' ' . $this->section_number;
$this->dublin_core->Relation = LAWS_NAME;
/*
* If the request specifies that rendered HTML should be returned, then generate that.
*/
if ( isset($this->config->render_html) && ($this->config->render_html === TRUE) )
{
$this->html = Law::render();
}
/*
* Provide a plain text version of this law.
*/
$this->plain_text = Law::render_plain_text();
/*
* Provide a plain text document header.
*/
$this->plain_text = str_repeat(' ', (round(((81 - strlen(LAWS_NAME)) / 2))))
. strtoupper(LAWS_NAME) . "\n\n"
. wordwrap(strtoupper($this->catch_line) . ' (' . SECTION_SYMBOL . ' '
. $this->section_number . ')', 80, "\n", TRUE)
. "\n\n" . $this->plain_text;
if (!empty($this->history))
{
$this->plain_text .= "\n" . wordwrap('HISTORY: ' . $this->history, 80, "\n", TRUE);
}
$law = $this;
unset($law->config);
return $law;
}
/**
* Return a listing of every section of the code that refers to a given section.
*/
function get_references()
{
/*
* We're going to need access to the database connection throughout this class.
*/
global $db;
/*
* If a section number doesn't exist in the scope of this class, then there's nothing to do.
*/
if (!isset($this->section_id))
{
return FALSE;
}
/*
* Get a listing of IDs, section numbers, and catch lines.
*/
$sql = 'SELECT DISTINCT laws.id, laws.section AS section_number, laws.catch_line
FROM laws
INNER JOIN laws_references
ON laws.id = laws_references.law_id
WHERE laws_references.target_law_id = :law_id
ORDER BY laws.order_by, laws.section ASC';
$sql_args = array(
':law_id' => $this->section_id
);
/*
* Execute the query.
*/
$statement = $db->prepare($sql);
$result = $statement->execute($sql_args);
/*
* If the query fails, or if no results are found, return false -- no sections refer to
* this one.
*/
if ( ($result === FALSE) || ($statement->rowCount() == 0) )
{
return FALSE;
}
/*
* Return the result as an enumerated object.
*/
$references = new stdClass();
$i = 0;
while ($reference = $statement->fetch(PDO::FETCH_OBJ))
{
$reference->catch_line = stripslashes($reference->catch_line);
$reference->url = 'http://' . $_SERVER['SERVER_NAME']
. ( ($_SERVER['SERVER_PORT'] == 80) ? '' : ':' . $_SERVER['SERVER_PORT'] )
. '/' . $reference->section_number . '/';
$references->$i = $reference;
$i++;
}
return $references;
}
/**
* Get a collection of the laws most similar to the present law.
*/
function get_related()
{
/*
* The number of results to return. The default is 5.
*/
if (!isset($this->num_results))
{
$this->num_results = 5;
}
/*
* Intialize Solarium.
*/
$client = new Solarium_Client($GLOBALS['solr_config']);
if ($client === FALSE)
{
return FALSE;
}
/*
* Create a MoreLikeThis query instance.
*/
$query = $client->createMoreLikeThis();
/*
* Note that we have to escape colons in this query.
*/
$query->setQuery('section:' . str_replace(':', '\:', $this->section_number));
$query->setMltFields('text,tags,catch_line');
$query->setMatchInclude(TRUE);
$query->setStart(0)->setRows($this->num_results);
/*
* Execute the query and return the result.
*/
$results = $client->select($query);
/*
* If our query fails.
*/
if ($results === FALSE)
{
return FALSE;
}
/*
* Create a new, blank object to store our related sections.
*/
$related = new StdClass();
/*
* Iterate through the returned documents
*/
$i=0;
foreach ($results as $document)
{
$law = new Law();
$law->law_id = $document->id;
$law->get_law();
$related->{$i} = $law;
$i++;
}
return TRUE;
}
/**
* Return the URL for a given section number or law ID.
*
* This is meant to be invoked inline (see its use in the get_related method), which is why it
* takes a section number as a parameter and returns a URL, rather than getting and setting
* those as object properties.
*/
function get_url($section_number)
{
/*
* We're going to need access to the database connection throughout this class.
*/
global $db;
/*
* If a section number hasn't been passed to this function, then there's nothing to do.
*/
if (empty($section_number))
{
return FALSE;
}
/*
* Prepare our SQL query.
*/
$sql = 'SELECT url
FROM permalinks
WHERE object_type="law"
AND token = :token';
$sql_args = array(
':token' => $section_number
);
$statement = $db->prepare($sql);
$result = $statement->execute($sql_args);
if ( ($result === FALSE) || ($statement->rowCount() == 0) )
{
return FALSE;
}
/*
* Return the result as an object.
*/
$permalink = $statement->fetch(PDO::FETCH_OBJ);
/*
* Return the permalink URL.
*/
return $permalink->url;
}
/**
* Record a view of a single law.
*/
function record_view()
{
/*
* If configured not to record views, then quietly exit.
*/
if ( defined('RECORD_VIEWS') && (RECORD_VIEWS === FALSE) )
{
return TRUE;
}
/*
* We're going to need access to the database connection throughout this class.
*/
global $db;
/*
* If a section number doesn't exist in the scope of this class, then there's nothing to do.
*/
if (!isset($this->section_number))
{
return FALSE;
}
/*
* Record the view.
*/
$sql = 'INSERT DELAYED INTO laws_views
SET section = :section';
$sql_args = array(
':section' => $this->section_number
);
if (!empty($_SERVER['REMOTE_ADDR']))
{
$sql .= ', ip_address=INET_ATON(:ip)';
$sql_args[':ip'] = $_SERVER['REMOTE_ADDR'];
}
/*
* Execute the query.
*/
$statement = $db->prepare($sql);
$result = $statement->execute($sql_args);
/*
* If the query fails, return false.
*/
if ($result === FALSE)
{
return FALSE;
}
return TRUE;
}
/**
* Get all metadata for a single law.
*/
function get_metadata()
{
/*
* We're going to need access to the database connection throughout this class.
*/
global $db;
/*
* If a section number doesn't exist in the scope of this class, then there's nothing to do.
*/
if (!isset($this->section_id))
{
return FALSE;
}
/*
* Get a listing of all metadata that belongs to this law.
*/
$sql = 'SELECT id, meta_key, meta_value
FROM laws_meta
WHERE law_id = :law_id';
$sql_args = array(
':law_id' => $this->section_id
);
$statement = $db->prepare($sql);
$result = $statement->execute($sql_args);
/*
* If the query fails, or if no results are found, return false -- no sections refer to this
* one.
*/
if ( ($result === FALSE) || ($statement->rowCount() == 0) )
{
return FALSE;
}
/*
* Return the result as an object.
*/
$metadata = $statement->fetchAll(PDO::FETCH_OBJ);
/*
* Create a new object, to which we will port a rotated version of this object.
*/
$rotated = new stdClass();
/*
* Iterate through the object in order to reorganize it, assigning the meta_key field to the
* key and the meta_value field to the value.
*/
foreach($metadata as $field)
{
/*
* If unserializing this value works, then we've got serialized data here.
*/
if (@unserialize($row->meta_value) !== FALSE)
{
$field->meta_value = unserialize($field->meta_value);
}
/*
* If JSON decoding this value works, then we've got JSON data here.
*/
if (@json_decode($row->meta_value) !== FALSE)
{
$field->meta_value = json_decode($field->meta_value);
}
/*
* Convert y/n values into TRUE/FALSE values.
*/
if ($field->meta_value == 'y')
{
$field->meta_value = TRUE;
}
elseif ($field->meta_value == 'n')
{
$field->meta_value = FALSE;
}
$rotated->{$field->meta_key} = $field->meta_value;
}
return $rotated;
}
/*
* Store a single piece of metadata for a single law
*
* Must receive $this->section_id and $this->metadata. The latter is an object that that
* contains a series of $key => $value pairs (at least one) that are to be stored for the law
* in question.
*
* This method exists within Law, as opposed to within the importer, because metadata can be
* stored at any time. For example, a list of court rulings affecting a given law wouldn't be
* imported only when the parser is run, because a court could issue a new ruling again at any
* time. Instead, that data is imported periodically, incrementally, via store_metadata.
*
* @param string $this->section_id The ID of the law.
* @param object $this->metadata Key/value pairs to be stored.
* @return TRUE or FALSE
*/
function store_metadata()
{
/*
* We're going to need access to the database connection throughout this class.
*/
global $db;
if ( !isset($this->section_id) || !is_object($this->metadata) )
{
return FALSE;
}
$sql = 'INSERT INTO laws_meta
SET law_id = :law_id,
meta_key = :meta_key,
meta_value = :meta_value,
date_created = now()';
$statement = $db->prepare($sql);
foreach ($this->metadata as $field)
{
$sql_args = array(
':law_id' => $this->section_id,
':meta_key' => $field->key,
':meta_value' => $field->value
);
$result = $statement->execute($sql_args);
if ($result === FALSE)
{
return FALSE;
}
}
return TRUE;
}
/**
* When provided with a section number, it indicates whether that section exists. This is
* designed for use when parsing the text of each section, which turns any section numbers into
* links. But it has to verify that they're really section numbers, and not strings that
* resemble section numbers, which necessitates a fast, lightweight function.
*/
function exists()
{
/*
* We're going to need access to the database connection throughout this class.
*/
global $db;
/*
* If neither a section number nor a law ID has been passed to this function, then there's
* nothing to do.
*/
if (!isset($this->section_number))
{
return FALSE;
}
/*
* Trim it down.
*/
$this->section_number = trim($this->section_number);
/*
* Query the database for the ID for this section number, retrieving the current version
* of the law.
*/
$sql = 'SELECT *
FROM laws
WHERE section = :section
AND edition_id = :edition_id';
$sql_args = array(
':section' => $this->section_number,
':edition_id' => EDITION_ID
);
$statement = $db->prepare($sql);
$result = $statement->execute($sql_args);
if ( ($result === FALSE) || ($statement->rowCount() < 1) )
{
return FALSE;
}
return TRUE;
}
/**
* Takes the instant law object and turns it into HTML, with embedded links, anchors, etc.
*/
function render()
{
/*
* Get the dictionary terms for this chapter.
*/
$dictionary = new Dictionary();
$dictionary->structure_id = $this->structure_id;
$dictionary->section_id = $this->section_id;
$tmp = $dictionary->term_list();
if ($tmp !== FALSE)
{
$terms = (array) $tmp;
unset($tmp);
}
/*
* If we've gotten a list of dictionary terms.
*/
if ( ($terms !== FALSE) && is_array($terms) )
{
/*
* Arrange our terms from longest to shortest. This is to ensure that the most specific
* terms are defined (e.g. "person of interest") rather than the broadest terms (e.g.
* "person").
*/
usort($terms, 'sort_by_length');
/*
* Store a list of the dictionary terms as an array, which is required for
* preg_replace_callback, the function that we use to insert the definitions.
*/
$term_pcres = array();
foreach ($terms as $term)
{
/*
* Step through each character in this word.
*/
for ($i=0; $i<strlen($term); $i++)
{
/*
* If there are any uppercase characters, then make this PCRE string case
* sensitive.
*/
if ( (ord($term{$i}) >= 65) && (ord($term{$i}) <= 90) )
{
$term_pcres[] = '/\b'.$term.'(s?)\b(?![^<]*>)/';
$caps = TRUE;
break;
}
}
/*
* If we have determined that this term does not contain capitalized letters, then
* create a case-insensitive PCRE string.
*/
if (!isset($caps))
{
$term_pcres[] = '/\b'.$term.'(s?)\b(?![^<]*>)/i';
}
/*
* Unset our flag -- we don't want to have it set the next time through.
*/
if (isset($caps))
{
unset($caps);
}
}
}
/*
* Instantiate our autolinker, which embeds links. If we've defined a state-custom
* autolinker, use that one. Otherwise, use the built-in one. Be sure not to attempt to
* autoload a file fitting our class-name schema, since this class, if it exists, would be
* found within class.[State].inc.php.
*/
if (class_exists('State_Autolinker', FALSE) === TRUE)
{
$autolinker = new State_Autolinker;
}
$autolinker = new Autolinker;
/*
* Iterate through every section to make some basic transformations.
*/
foreach ($this->text as $section)
{
/*
* Prevent lines from wrapping in the middle of a section identifier.
*/
$section->text = str_replace('§ ', '§&nbsp;', $section->text);
/*
* Turn every code reference in every paragraph into a link.
*/
$section->text = preg_replace_callback(SECTION_REGEX, array($autolinker, 'replace_sections'), $section->text);
/*
* Turn every pair of newlines into carriage returns.
*/
$section->text = preg_replace('/\R\R/', '<br /><br />', $section->text);
/*
* Use our dictionary to embed dictionary terms in the form of span titles.
*/
if (isset($term_pcres))
{
$section->text = preg_replace_callback($term_pcres, array($autolinker, 'replace_terms'), $section->text);
}
}
$html = '';
/*
* Iterate through each section of text to display it.
*/
$i=0;
$num_paragraphs = count((array) $this->text);
foreach ($this->text as $paragraph)
{
/*
* Identify the prior and next sections, by storing their prefixes.
*/
if ($i > 0)
{
$paragraph->prior_prefix = $this->text->{$i-1}->entire_prefix;
}
if ( ($i+1) < $num_paragraphs )
{
$paragraph->next_prefix = $this->text->{$i+1}->entire_prefix;
}
/*
* If this paragraph's prefix hierarchy is different than that of the prior prefix, then
* indicate that this is a new section.
*/
if ( !isset($paragraph->prior_prefix) || ($paragraph->entire_prefix != $paragraph->prior_prefix) )
{
$html .= '
<section';
if (!empty($paragraph->prefix_anchor))
{
$html .= ' id="' . $paragraph->prefix_anchor . '"';
}
/*
* If this is a subsection, indent it.
*/
if ($paragraph->level > 1)
{
$html .= ' class="indent-' . ($paragraph->level-1) . '"';
}
$html .= '>';
}
/*
* Start a paragraph of the appropriate type.
*/
if ($paragraph->type == 'section')
{
$html .= '<p>';
}
elseif ($paragraph->type == 'table')
{
$html .= '<div class="tabular"><pre class="table">';
}
/*
* If we've got a section prefix, and it's not the same as the last one, then display
* it.
*/
if ( !empty($paragraph->prefix)
&&
( !isset($paragraph->prior_prefix) || ($paragraph->entire_prefix != $paragraph->prior_prefix) ) )
{
$html .= $paragraph->prefix;
/*
* We could use a regular expression to determine if we need to append a period, but
* that would be slower.
*/
if ( (substr($paragraph->prefix, -1) != ')') && (substr($paragraph->prefix, -1) != '.') )
{
$html .= '.';
}
$html .= ' ';
}
/*
* Display this section of text. Purely structural sections lack text of their own (only
* their child structures contain text), which is why this is conditional.
*/
if (!empty($paragraph->text))
{
$html .= $paragraph->text;
}
/*
* If we've got a section prefix, append a paragraph link to the end of this section.
*/
if ( (!empty($paragraph->prefix) || !empty($paragraph->id)) && !defined('EXPORT_IN_PROGRESS'))
{
/*
* Assemble the permalink
*/
if($paragraph->prefix_anchor)
{
$permalink = $_SERVER['REQUEST_URI'] . '#'
. $paragraph->prefix_anchor;
}
else
{
$permalink = $_SERVER['REQUEST_URI'] . '#'
. $paragraph->id;
}
$html .= ' <a id="paragraph-' . $paragraph->id . '" class="section-permalink" '
.'href="' . $permalink . '"><i class="icon-link"></i></a>';
}
if ($paragraph->type == 'section')
{
$html .= '</p>';
}
elseif ($paragraph->type == 'table')
{
$html .= '</pre></div>';
}
/*
* If our next prefix is different than the current prefix, than terminate this section.
*/
if (
( !isset($paragraph->next_prefix) || ($paragraph->entire_prefix != $paragraph->next_prefix) )
||
( ($i+1) === $num_paragraphs)
)
{
$html .= '</section>';
}
$i++;
}
return $html;
} // end render()
/**
* Takes the instant law object and turns it into a nicely formatted plain text version.
*/
function render_plain_text()
{
if (!isset($this->text))
{
return FALSE;
}
/*
* Iterate through every section to make some basic transformations.
*/
foreach ($this->text as $section)
{
/*
* Prevent lines from wrapping in the middle of a section identifier by replacing the
* &nbsp; entity with the Unicode NO-BREAK-SPACE (U+00A0) character.
*/
$section->text = str_replace('§&nbsp;', '§ ', $section->text);
/*
* Eliminate any HTML.
*/
$section->text = strip_tags($section->text);
}
/*
* Instantiate the variable in which we'll store the plain text.
*/
$text = '';
/*
* Iterate through each section of text to display it.
*/
$i=0;
$num_paragraphs = count((array) $this->text);
foreach ($this->text as $paragraph)
{
/*
* Initialize a variable that we'll use to store the text for this subsection.
*/
$subsection = '';
/*
* If we've got a section prefix, and it's not the same as the last one, then display
* it.
*/
if ( !empty($paragraph->prefix)
&&
( !isset($paragraph->prior_prefix) || ($paragraph->entire_prefix != $paragraph->prior_prefix) ) )
{
$subsection .= $paragraph->prefix;
/*
* We could use a regular expression to determine if we need to append a period, but
* that would be slower.
*/
if ( (substr($paragraph->prefix, -1) != ')') && (substr($paragraph->prefix, -1) != '.') )
{
$subsection .= '.';
}
$subsection .= ' ';
}
/*
* Add the text itself to the subsection.
*/
$subsection .= $paragraph->text;
/*
* Wrap this text at 80 characters minus two spaces for every nested subsection,
* breaking up words that exceed the line length.
*/
$subsection = wordwrap($subsection, (80 - (($paragraph->level - 1) * 2)), "\n", TRUE);
/*
* Indent applicable subsections by adding blank space to the beginning of each line.
*/
if ($paragraph->level > 0)
{
$lines = explode("\n", $subsection);
foreach ($lines as &$line)
{
$line = str_repeat(' ', ( ($paragraph->level - 1) * 3 )) . $line;
}
$subsection = implode("\n", $lines);
}
/*
* Finish up with a pair of newlines.
*/
$subsection .= "\n\n";
/*
* And, finally, add this subsection to the text of the section.
*/
$text .= $subsection;
$i++;
}
/*
* Hack off any trailing (or, somehow, leading) whitespace, and finish with a single
* newline.
*/
$text = trim($text) . "\n";
return $text;
} // end render_plain_text()
} // end Law
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment