/** * * @see sfValidatorBase * @param mixed $value * @return mixed */ protected function doClean($value) { $clean = (string) parent::doClean($value); $clean = aString::strtolower($clean); $slugified = aTools::slugify($clean, $this->getOption('allow_slashes')); if ($this->getOption('strict')) { if ($slugified !== $clean) { throw new sfValidatorError($this, 'invalid', array('value' => $value)); } } else { $clean = $slugified; } if ($this->getOption('require_leading_slash') && substr($value, 0, 1) !== '/') { throw new sfValidatorError($this, 'invalid', array('value' => $value)); } return $clean; }
?> <?php // It is amazing how often this works well even for something as short as ?> <?php // 'Blockley Hall' since the user's location is often known to Google Maps. However ?> <?php // it is less useful if all of your locations are 'room 150', etc. with no further ?> <?php // information. Naturally full addresses work best ?> <li class="post-location a-ui"> <?php echo aString::firstLine($aEvent['location']); ?> <?php if (sfConfig::get('app_events_google_maps', true)) { ?> <?php echo a_button(a_('Google Maps'), url_for('http://maps.google.com/maps?' . http_build_query(array('q' => preg_replace('/\\s+/', ' ', $aEvent['location'])))), array('no-bg', 'alt', 'icon', 'a-google-maps')); ?> <?php } ?> </li> <?php } ?>
/** * * Limits the number of characters in a string. * @param string $string * @param uint $character_limit * maximum number of characters to return, inclusive of any added ellipsis * NOTE: this is characters, not bytes (think UTF8). Be generous with columns * @param optional array * if $options['append_ellipsis'] is set, append that string to the end * of strings that have been truncated * @return string * new string containing only characters up to the limit * Suitable when a word count limit is not enough (because words are * sometimes unreasonably long). * Tries to preserve word boundaries, but not too hard, as very long words can * create problems of their own. */ public static function limitCharacters($s, $length, $options = array()) { $ellipsis = ""; if (isset($options['append_ellipsis']) && $options['append_ellipsis']) { $ellipsis = "..."; } if ($length < 12) { // Not designed to be elegant below this length return aString::substr($s, 0, $length); } if (aString::strlen($s) > $length) { $s = aString::substr($s, 0, $length - aString::strlen($ellipsis)); $slength = aString::strlen($s); for ($i = 1; $i <= 10; $i++) { $c = aString::substr($s, $slength - $i, 1); if ($c === ' ' || $c === '\\t' || $c === '\\r' || $c === '\\n') { return aString::substr($s, 0, $slength) . $ellipsis; } } return $s . $ellipsis; } return $s; }
public function getSearchSummary() { return aString::limitWords($this->getSearchText(false), sfConfig::get('app_a_search_summary_wordcount', 50), "..."); }
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 $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(); } } } }
/** * * @param string $areas Array of areas to retrieve text for * @param int $limit Number of characters to restrict retrieval to * @return string */ public function getTextForAreas($areas = array(), $limit = null, $options = null) { if (is_null($options)) { $options = array('append_ellipsis' => true); } $text = ''; foreach ($areas as $area) { foreach ($this->Page->getArea($area) as $slot) { if (method_exists($slot, 'getText')) { $text .= $slot->getText(); } } } if (!is_null($limit)) { $text = aString::limitWords($text, $limit, $options); } return $text; }
/** * UTF-8 where available. If your UTF-8 gets munged make sure your PHP has the * mbstring extension. allowSlashes will allow / but will reduce duplicate / and * remove any / at the end. Everything that isn't a letter or a number * (or a slash, when allowed) is converted to a -. Consecutive -'s are reduced and leading and * trailing -'s are removed * $betweenWords must not contain characters that have special meaning in a regexp. * Usually it is - (the default) or ' ' * @param mixed $path * @param mixed $allowSlashes * @param mixed $allowUnderscores * @param mixed $betweenWords * @return mixed */ public static function slugify($path, $allowSlashes = false, $allowUnderscores = true, $betweenWords = '-') { // This is the inverse of the method above if (function_exists('mb_strtolower')) { // UTF-8 capable replacement for \W. Works fine for English and also for Greek, etc. // ... Except when PCRE is built without unicode properties and PHP can't tell! We'll // put that in servercheck.php $alnum = '\\p{L}\\p{N}' . ($allowUnderscores ? '_' : ''); $modifier = 'u'; } else { $alnum = $allowUnderscores ? '\\w' : '[A-Za-z0-9]'; $modifier = ''; } if ($allowSlashes) { $alnum .= '\\/'; } // Removing - here expands flexibility and shouldn't hurt because it's the replacement anyway $regexp = "/[^{$alnum}]+/{$modifier}"; $path = aString::strtolower(preg_replace($regexp, $betweenWords, $path)); if ($allowSlashes) { // No multiple consecutive / $path = preg_replace("/\\/+/{$modifier}", "/", $path); // No trailing / unless it's the homepage if ($path !== '/') { $path = preg_replace("/\\/\$/{$modifier}", '', $path); } } // No consecutive dashes $path = preg_replace("/{$betweenWords}+/{$modifier}", $betweenWords, $path); // Leading and trailing dashes are silly. This has the effect of trim() // among other sensible things $path = preg_replace("/^-*(.*?)-*\$/{$modifier}", '$1', $path); return $path; }
/** * 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; }
/** * 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); } }