/** * DOCUMENT ME * @param sfRequest $request * @return mixed */ public function executeEdit(sfRequest $request) { $this->editSetup(); $value = $this->getRequestParameter('slot-form-' . $this->id); $this->options['multiline'] = $this->getOption('multiline', true); $this->form = new aTextForm($this->id, $this->slot->value, $this->options); $this->form->bind($value); if ($this->form->isValid()) { // TODO: this might make a nice validator $value = $this->form->getValue('value'); if (!$this->options['multiline']) { $value = preg_replace("/\\s/", " ", $value); } // We store light markup for "plain text" slots. We DO NOT store the mailto: obfuscation though $value = aHtml::textToHtml($value); $maxlength = $this->getOption('maxlength'); if ($maxlength !== false) { $value = substr(0, $maxlength); } $this->slot->value = $value; $result = $this->editSave(); return $result; } else { // Makes $this->form available to the next iteration of the // edit view so that validation errors can be seen (although there // aren't any in this case) return $this->editRetry(); } }
public function executeNormalView() { $this->setup(); // We don't recommend doing this at the FCK level, // let it happen here instead so what is stored in the // db can be clean markup $this->value = aHtml::obfuscateMailto($this->value); }
/** * DOCUMENT ME */ public function executeNormalView() { $this->setup(); // Yes, we store basic HTML markup for "plaintext" slots. // However we obfuscate the mailto links on the fly as a last step // (so it's not as fast as we originally intended, but this is an // essential feature that makes transformation of the code difficult). $this->value = aHtml::obfuscateMailto($this->value); }
/** * @see sfValidatorBase */ protected function doClean($value) { $clean = (string) $value; if ($this->getOption('strip')) { $clean = aHtml::simplify($clean, $this->getOptionOrFalse('allowed_tags'), $this->getOptionOrFalse('complete'), $this->getOptionOrFalse('allowed_attributes'), $this->getOptionOrFalse('allowed_styles')); } else { throw new sfException('That should not happen strip is set in configure in sfValidatorHtml'); } $clean = parent::doClean($clean); return $clean; }
public function getSearchText() { // Convert from HTML to plaintext before indexing by Lucene // However first add line breaks after certain tags for better formatting // (this method is also used for generating informational diffs between versions). // This is a noncritical feature so it doesn't have to be as precise // as strip_tags and shouldn't try to substitute for it in the matter of // actually removing the tags $this->value = preg_replace("/(<p>|<br.*?>|<blockquote>|<li>|<dt>|<dd>|<nl>|<ol>)/i", "\$1\n", $this->value); return aHtml::toPlaintext($this->value); }
/** * DOCUMENT ME * @param mixed $values * @return mixed */ public function updateObject($values = null) { $object = parent::updateObject($values); // Do some postvalidation of what parent::updateObject did // (it would be nice to turn this into an sfValidator subclass) $object->setDescription(aHtml::simplify($object->getDescription(), "<p><br><b><i><strong><em><ul><li><ol><a>")); // The tags field is not a native Doctrine field // so we can't rely on parent::updateObject to sort out // whether to use $values or $this->getValue. So we need // to sanitize and figure out what set of values to use // (embedded forms get a $values parameter, non-embedded // use $this->values) if (is_null($values)) { $values = $this->values; } // Slashes break routes in most server configs. Do NOT force case of tags. $values['tags'] = str_replace('/', '-', isset($values['tags']) ? $values['tags'] : ''); $object->setTags($values['tags']); return $object; }
public function updateObject($values = null) { $object = parent::updateObject($values); // Do some postvalidation of what parent::updateObject did // (it would be nice to turn this into an sfValidator subclass) $object->setDescription(aHtml::simplify($object->getDescription(), "<p><br><b><i><strong><em><ul><li><ol><a>")); // The tags field is not a native Doctrine field // so we can't rely on parent::updateObject to sort out // whether to use $values or $this->getValue. So we need // to sanitize and figure out what set of values to use // (embedded forms get a $values parameter, non-embedded // use $this->values) if (is_null($values)) { $values = $this->values; } // Now we're ready to play // We like all-lowercase tags for consistency $values['tags'] = strtolower($values['tags']); $object->setTags($values['tags']); $object->setOwnerId(sfContext::getInstance()->getUser()->getGuardUser()->getId()); return $object; }
/** * * @param string $areas Array of areas to retrieve text for * @param int $limit Number of characters to restrict retrieval to * @return string */ public function getRichTextForAreas($areas = array(), $limit = null) { $text = ''; if (!is_array($areas)) { $areas = array($areas); } foreach ($areas as $area) { foreach ($this->Page->getArea($area) as $slot) { if ($slot['type'] == 'aRichText' || $slot['type'] == 'aText') { $text .= $slot->getValue(); } } } if (!is_null($limit)) { $text = aHtml::limitWords($text, $limit, array('append_ellipsis' => true)); } return $text; }
/** * DOCUMENT ME * @param SimpleXMLElement $slot * @return mixed */ protected function parseSlotForeignHtml(SimpleXMLElement $slot, $title = null, &$counters = null) { $n = 1; $html = $slot->value->__toString(); $segments = aString::splitAndCaptureAtEarliestMatch($html, array('/\\<a href=\\"[^\\"]+\\"[^\\>]*>\\s*(?:\\<br \\/\\>| |\\s)*\\<img.*?src="[^\\"]+[^\\>]*\\>(?:\\<br \\/\\>| |\\s)*\\<\\/a\\>/is', '/\\<img.*?src="[^\\"]+".*?\\>/is', '/\\<object.*?\\>.*?\\<\\/object\\>/is', '/\\<iframe.*?\\>.*?\\<\\/iframe\\>/is')); foreach ($segments as $segment) { $mediaItem = null; if (preg_match('/\\<object.*?\\>.*?\\<\\/object\\>|\\<iframe.*?\\>.*?\\<\\/iframe\\>/is', $segment)) { $form = new aMediaVideoForm(); $result = $form->classifyEmbed($segment); if ($result['ok']) { $info = array('title' => isset($result['serviceInfo']['title']) ? $result['serviceInfo']['title'] : $title . ' video ' . $n, 'embed' => $result['embed'], 'width' => isset($result['width']) ? $result['width'] : null, 'height' => isset($result['height']) ? $result['height'] : null, 'format' => isset($result['format']) ? $result['format'] : null, 'type' => 'video', 'tags' => isset($result['serviceInfo']['tags']) ? preg_split('/\\s*,\\s*/', $result['serviceInfo']['tags']) : array(), 'service_url' => isset($result['serviceInfo']['url']) ? $result['serviceInfo']['url'] : null); $mediaId = $this->findOrAddVideo($info); $slotInfos[] = array('type' => 'aVideo', 'mediaId' => $mediaId); $n++; } } elseif (preg_match('/<img.*?src="(.*?)".*?>/is', $segment, $matches)) { $src = $matches[1]; // & won't work if we don't decode it to & before passing it to the server $src = html_entity_decode($src); $mediaId = $this->findOrAddMediaItem($src, 'id'); if (preg_match('/href="(.*?)"/', $segment, $matches)) { $url = $matches[1]; } // $mediaItem->save(); if (!is_null($mediaId)) { $slotInfo = array('type' => 'aImage', 'mediaId' => $mediaId, 'value' => array()); if (isset($url)) { $slotInfo = array('type' => 'aButton', 'value' => array('url' => $url, 'title' => ''), 'mediaId' => $mediaId); } $slotInfos[] = $slotInfo; } } else { $slotInfos[] = array('type' => 'aRichText', 'value' => aHtml::simplify($segment)); } } return $slotInfos; }
/** * * This function returns a basic HTML representation of your slot's comments * (passing the default settings of aHtml::simplify, for instance). Used for Google Calendar * buttons, RSS feeds and similar * @return string */ public function getBasicHtml() { return aHtml::simplify($this->value); }
/** * This is a quick and dirty implementation based on calling limitWords * with an optimistic guess and then backing off a few times if necessary * until we get under the byte limit. Note that limitBytes is designed * to fit things in buffers, not save screen space, so it does have to * make sure the result is not too big * @param mixed $string * @param mixed $byte_limit * @param mixed $options * @return mixed */ public static function limitBytes($string, $byte_limit, $options = array()) { $word_limit = (int) ($byte_limit / 8); while (true) { $s = aHtml::limitWords($string, $word_limit, $options); if (strlen($s) <= $byte_limit) { break; } $word_limit = (int) ($word_limit * 0.75); } return $s; }
public function executeIcalFeed(sfWebRequest $request) { $this->buildParams(); $this->dateRange = ''; $this->aEvent = $this->getRoute()->getObject(); $this->categories = aCategoryTable::getCategoriesForPage($this->page); $this->forward404Unless($this->aEvent); $this->forward404Unless($this->aEvent['status'] == 'published' || $this->getUser()->isAuthenticated()); aBlogItemTable::populatePages(array($this->aEvent)); header("Content-type: text/calendar"); header('Content-disposition: attachment; filename=' . str_replace('.', '-', $this->getRequest()->getHost() . '-' . $this->aEvent->id) . '.ics'); $start = $this->aEvent->getVcalStartDateTime(); $end = $this->aEvent->getVcalEndDateTime(); $title = aString::toVcal(aHtml::toPlaintext($this->aEvent->getTitle())); $body = aString::toVcal(aHtml::toPlaintext($this->aEvent->Page->getAreaText('blog-body'))); echo <<<EOM BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT CATEGORIES:MEETING DTSTART:{$start} DTEND:{$end} SUMMARY:{$title} DESCRIPTION:{$body} CLASS:PRIVATE END:VEVENT END:VCALENDAR EOM; exit(0); }
/** * DOCUMENT ME * @param mixed $areaname * @param mixed $word_limit * @return mixed */ public function getAreaBasicHtml($areaname, $word_limit = false) { $slots = $this->getSlotsByAreaName($areaname); $text = ''; foreach ($slots as $slot) { if (strlen($text)) { // div might not be permitted in a lot of 'basic html' // contexts, but we do need some vertical break between // two slots to reasonably reproduce what // Apostrophe does with them $text .= '<br />'; } $text .= $slot->getBasicHtml(); } if ($word_limit) { return aHtml::limitWords($text, $word_limit, array('append_ellipsis' => true)); } else { return $text; } }
<?php // Google says they accept HTML in descriptions but they seem not to use it for anything. // You never see descriptions anyway except when editing them. So send the plaintext. // The byte limit was chosen to avoid creating a URL that the browser won't accept, // even when Google double-encodes it in some situations $aEvent = $sf_data->getRaw('aEvent'); echo a_button(a_('Add to Google Calendar'), url_for('http://www.google.com/calendar/event?' . http_build_query(array('action' => 'TEMPLATE', 'text' => $aEvent->getTitle(), 'dates' => $aEvent->getUTCDateRange(), 'location' => preg_replace('/\\s+/', ' ', $aEvent['location']), 'sprop' => 'website:' . $sf_request->getHost(), 'details' => aHtml::toPlaintext($aEvent->getTextForArea('blog-body', 500, array('characters' => true, 'append_ellipsis' => a_('...'))))))), array('icon', 'no-bg', 'alt', 'a-events'));
</a> <?php } else { ?> <?php echo $feedItem->getTitle(); ?> <?php } ?> </li> <?php $date = $feedItem->getPubDate(); ?> <?php if ($date) { ?> <li class="date"><?php echo $dateFormat ? date($dateFormat, $date) : aDate::pretty($date) . ' ' . aDate::time($date); ?> </li> <?php } ?> <li class="description"><?php echo auto_link_text(aHtml::simplify($feedItem->getDescription(), $markup, false, isset($attributes) ? $attributes : false, isset($styles) ? $styles : false)); ?> </li> </ul> </li>
/** * DOCUMENT ME * @param mixed $arguments * @param mixed $options */ protected function execute($arguments = array(), $options = array()) { // We need a basic context so we can call helpers to format text $context = sfContext::createInstance($this->configuration); // initialize the database connection $databaseManager = new sfDatabaseManager($this->configuration); $connection = $databaseManager->getDatabase($options['connection'] ? $options['connection'] : null)->getConnection(); // PDO connection not so useful, get the doctrine one $conn = Doctrine_Manager::connection(); $accounts = Doctrine::getTable('aEmbedMediaAccount')->findAll(); foreach ($accounts as $a) { $perPage = 50; $service = aMediaTools::getEmbedService($a->service); if (!$service) { // An account for a service that has been deconfigured continue; } $total = null; $page = 1; $serviceUrls = array(); while (true) { $results = $service->browseUser($a->username, $page, $perPage); if ($results === false) { // We hit the rate limit, the account is bad, etc. Just // be tolerant and retry later. Would be nice to distinguish // these cases but it's not that hard to figure out an // account is gone break; } foreach ($results['results'] as $result) { $serviceUrls[] = $result['url']; } // We hit the end of the results for this user if (!count($results['results'])) { break; } $page++; } if (count($serviceUrls)) { $existingServiceUrls = Doctrine::getTable('aMediaItem')->createQuery('m')->select('m.service_url')->andWhereIn('m.service_url', $serviceUrls)->execute(array(), Doctrine::HYDRATE_SINGLE_SCALAR); } else { $existingServiceUrls = array(); } $existingServiceUrls = array_flip($existingServiceUrls); foreach ($serviceUrls as $serviceUrl) { if (!isset($existingServiceUrls[$serviceUrl])) { // If Doctrine becomes a performance problem I could use PDO // and set lucene_dirty to let that clean itself up later $id = $service->getIdFromUrl($serviceUrl); $info = $service->getInfo($id); if (!$info) { // We are not actually allowed meaningful access to this video. Password protected for example continue; } $item = new aMediaItem(); $item->setTitle($info['title']); // We want tags to be lower case, and slashes break routes in most server configs. $info['tags'] = str_replace('/', '-', aString::strtolower($info['tags'])); $item->setTags($info['tags']); $item->setDescription(aHtml::textToHtml($info['description'])); $item->setCredit($info['credit']); $item->setServiceUrl($info['url']); $item->setType($service->getType()); // The dance is this: get the thumbnail if there is one; // call preSaveFile to learn the width, height and format // before saving; save; and then saveFile to copy it to a // filename based on the slug, which is unknown until after save $thumbnail = $service->getThumbnail($id); if ($thumbnail) { // Grab a local copy of the thumbnail, and get the pain // over with all at once in a predictable way if // the service provider fails to give it to us. $thumbnailCopy = aFiles::getTemporaryFilename(); if (copy($thumbnail, $thumbnailCopy)) { $item->preSaveFile($thumbnailCopy); } } $item->save(); if ($thumbnail) { $item->saveFile($thumbnailCopy); } $item->free(); } } } }
/** * DOCUMENT ME * @param sfWebRequest $request * @return mixed */ public function executeEditVideo(sfWebRequest $request) { $this->forward404Unless(aMediaTools::userHasUploadPrivilege()); $item = null; $this->slug = false; $this->popularTags = PluginTagTable::getPopulars(null, array('sort_by_popularity' => true), false, 10); if (sfConfig::get('app_a_all_tags', true)) { $this->allTags = PluginTagTable::getAllTagNameWithCount(); } else { $this->allTags = array(); } if ($request->hasParameter('slug')) { $item = $this->getItem(); $this->slug = $item->getSlug(); } if ($item) { $this->forward404Unless($item->userHasPrivilege('edit')); } $this->item = $item; $embed = false; $parameters = $request->getParameter('a_media_item'); if ($parameters) { $files = $request->getFiles('a_media_item'); $this->form = new aMediaVideoForm($item); if (isset($parameters['embed'])) { // We need to do some prevalidation of the embed code so we can prestuff the // file, title, tags and description widgets $result = $this->form->classifyEmbed($parameters['embed']); if (isset($result['thumbnail'])) { $thumbnail = $result['thumbnail']; if (!isset($parameters['title']) && !isset($parameters['tags']) && !isset($parameters['description']) && !isset($parameters['credit'])) { $parameters['title'] = $result['serviceInfo']['title']; // We want tags to be lower case, and slashes break routes in most server configs. $parameters['tags'] = str_replace('/', '-', aString::strtolower($result['serviceInfo']['tags'])); $parameters['description'] = aHtml::textToHtml($result['serviceInfo']['description']); $parameters['credit'] = $result['serviceInfo']['credit']; } } } // On the first pass with a youtube video we just make the service's thumbnail the // default thumbnail. We don't force them to use it. This allows more code reuse // (Moving this after the bind is necessary to keep it from being overwritten) if (isset($thumbnail)) { // OMG file widgets can't have defaults! Ah, but our persistent file widget can $tmpFile = aFiles::getTemporaryFilename(); file_put_contents($tmpFile, file_get_contents($thumbnail)); $vfp = new aValidatorFilePersistent(); $guid = aGuid::generate(); $vfp->clean(array('newfile' => array('tmp_name' => $tmpFile), 'persistid' => $guid)); // You can't mess about with widget defaults after a bind, but you // *can* tweak the array you're about to bind with $parameters['file']['persistid'] = $guid; } $this->form->bind($parameters, $files); do { // first_pass forces the user to interact with the form // at least once. Used when we're coming from a // YouTube search and we already technically have a // valid form but want the user to think about whether // the title is adequate and perhaps add a description, // tags, etc. if ($this->hasRequestParameter('first_pass') || !$this->form->isValid()) { break; } $thumbnail = $this->form->getValue('file'); // The base implementation for saving files gets confused when // $file is not set, a situation that our code tolerates as useful // because if you're updating a record containing an image you // often don't need to submit a new one. unset($this->form['file']); $object = $this->form->getObject(); if ($thumbnail) { $object->preSaveFile($thumbnail->getTempName()); } $this->form->save(); if ($thumbnail) { $object->saveFile($thumbnail->getTempName()); } return $this->redirect("aMedia/resumeWithPage"); } while (false); } }
<li class="a-feed-item"> <ul> <li class="title"><?php echo link_to_if($feedItem->getLink() && $links, $feedItem->getTitle(), $feedItem->getLink()); ?> </li> <?php $date = $feedItem->getPubDate(); ?> <li class="date"><?php echo $dateFormat ? date($dateFormat, $date) : aDate::pretty($date) . ' ' . aDate::time($date); ?> </li> <li class="description"><?php echo aHtml::simplify($feedItem->getDescription(), $markup); ?> </li> </ul> </li>
/** * * @param string $areas Array of areas to retrieve text for * @param int $limit Number of characters to restrict retrieval to * @return string */ public function getRichTextForAreas($areas = array(), $limit = null) { $text = ''; if (!is_array($areas)) { $areas = array($areas); } foreach ($areas as $area) { $text .= $this->Page->getAreaBasicHtml($area); } if (!is_null($limit)) { $text = aHtml::limitWords($text, $limit, array('append_ellipsis' => true)); } return $text; }