public function parse_htpasswd($passwdfile)
 {
     $lines = file($passwdfile);
     $users = array();
     $i = 0;
     foreach ($lines as $line) {
         // if there is no :, assume this is a username with a newline
         if (MultiByte::strpos($line, ':') === false) {
             // append the next line to it
             $line = $line . $lines[$i + 1];
             // unset the next line
             unset($lines[$i + 1]);
         }
         list($username, $password) = explode(':', $line);
         // trim the username and password
         $username = trim($username);
         $password = trim($password);
         if ($username == '' || $password == '') {
             continue;
         }
         $users[$username] = $password;
         $i++;
     }
     return $users;
 }
	/**
	 * Respond to Javascript callbacks
	 * The name of this method is action_ajax_ followed by what you passed to the context parameter above.
	 */
	public function action_ajax_auto_tags( $handler )
	{
		$selected = array();
		if( isset( $handler->handler_vars['selected'] ) ) {
			$selected = Utils::single_array( $handler->handler_vars['selected'] );
		}
		if( isset( $handler->handler_vars['term'] ) && MultiByte::strlen( $handler->handler_vars['term'] ) ) {
			$search = $handler->handler_vars['term'] . '%';
			$tags = new Terms( DB::get_results( "SELECT * FROM {terms} WHERE vocabulary_id = :vid and LOWER(term_display) LIKE LOWER(:crit) ORDER BY term_display ASC", array( 'vid' => Tags::vocabulary()->id, 'crit' => $search ), 'Term' ) );
		}
		else {
			$tags = Tags::vocabulary()->get_tree( 'term_display ASC' );
		}

		$resp = array();
		foreach ( $tags as $tag ) {
			$resp[] = MultiByte::strpos( $tag->term_display, ',' ) === false ? $tag->term_display : $tag->tag_text_searchable;
		}

		if( count( $selected ) ) {
			$resp = array_diff($resp, $selected );
		}
		// Send the response
//		$ar = new AjaxResponse();
//		$ar->data = $resp;
//		$ar->out();
		echo json_encode( $resp );
	}
Example #3
0
 function test_strtolower()
 {
     $this->assert_equal(MultiByte::strtolower(self::$test_str), mb_strtolower(mb_convert_encoding(self::$test_str, 'UTF-8', mb_detect_encoding(self::$test_str)), 'UTF-8'));
     printf("Test string: %s <br>", self::$test_str);
     printf("MultiByte strtolower: %s <br>", MultiByte::strtolower(self::$test_str));
     printf("mbstring strtolower without detecting encoding: %s <br>", mb_strtolower(self::$test_str));
     printf("mstring strtolower with detecting encoding: %s <br><br>", mb_strtolower(self::$test_str, mb_detect_encoding(self::$test_str)));
 }
Example #4
0
 /**
  * Perform all filtering, return new string.
  * @param string $str Input string.
  * @return string Filtered output string.
  */
 public static function filter($str)
 {
     if (!MultiByte::valid_data($str)) {
         return '';
     } else {
         do {
             $_str = $str;
             $str = self::strip_nulls($str);
             $str = self::strip_illegal_entities($str);
             $str = self::filter_html_elements($str);
         } while ($str != $_str);
         return $str;
     }
 }
Example #5
0
 /**
  * Create the admin theme instance
  *
  * @param string $page The admin page requested
  * @param string $type The content type included in the request
  */
 public function setup_admin_theme($page, $type = '')
 {
     if (!isset($this->theme)) {
         $theme_dir = Plugins::filter('admin_theme_dir', Site::get_dir('admin_theme', true));
         $this->theme = Themes::create('_admin', 'RawPHPEngine', $theme_dir);
         // Add some default template variables
         $this->set_admin_template_vars($this->theme);
         $this->theme->admin_type = $type;
         $this->theme->admin_page = $page;
         $this->theme->admin_page_url = $page == 'dashboard' ? URL::get('admin', 'page=') : URL::get('admin', 'page=' . $page);
         $this->theme->page = $page;
         $this->theme->admin_title = MultiByte::ucwords($page) . ($type != '' ? ' ' . MultiByte::ucwords($type) : '');
         $this->theme->admin_title = isset($this->theme->mainmenu[$this->theme->admin_page]['text']) ? $this->theme->mainmenu[$this->theme->admin_page]['text'] : MultiByte::ucwords($page) . ($type != '' ? ' ' . MultiByte::ucwords($type) : '');
     }
 }
Example #6
0
	/**
	 * Turns a comma-separated string or array of terms into an array of Term objects
	 * @param mixed $terms A comma-separated string or array of string terms
	 * @param string $term_class The class of the Term object type to create from each string
	 * @param Vocabulary $vocabulary An instance of the Vocabulary that might hold the terms.  
	 * 	 Use existing term object data if found in the specified vocabulary.   	 
	 * @return Terms An instance of Terms contianing the specified Term objects
	 **/
	public static function parse( $terms, $term_class = 'Term', $vocabulary = null )
	{
		if ( is_string( $terms ) ) {
			if ( '' === $terms ) {
				return new Terms();
			}
			$terms = trim( MultiByte::str_replace( '&quot;', '"', $terms ) );
			// dirrty ;)
			$rez = array( '\\"'=>':__unlikely_quote__:', '\\\''=>':__unlikely_apos__:' );
			$zer = array( ':__unlikely_quote__:'=>'"', ':__unlikely_apos__:'=>"'" );
			// escape
			$tagstr = str_replace( array_keys( $rez ), $rez, $terms );
			// match-o-matic
			preg_match_all( '/((("|((?<= )|^)\')\\S([^\\3]*?)\\3((?=[\\W])|$))|[^,])+/u', $tagstr, $matches );
			// cleanup
			$terms = array_map( 'trim', $matches[0] );
			$terms = preg_replace( array_fill( 0, count( $terms ), '/^(["\'])(((?!").)+)(\\1)$/' ), '$2', $terms );
			// unescape
			$terms = str_replace( array_keys( $zer ), $zer, $terms );
			// hooray
		}
		if ( is_array( $terms ) ) {
			if ( $vocabulary instanceof Vocabulary ) {
				foreach ( $terms as $k => $term ) {
					if ( $saved_term = $vocabulary->get_term( $term, $term_class ) ) {
						$terms[$k] = $saved_term;
					}
					else {
						$terms[$k] = new $term_class( $term );
					}
				}
//Utils::debug($terms);
			}
			else {
				array_walk( $terms, create_function( '&$tag', '$tag = new ' . $term_class . '($tag);' ) );
			}
			return new Terms( $terms );
		}
		return new Terms();
	}
Example #7
0
 /**
  * Parses a search string for status, type, author, and tag keywords. Returns
  * an associative array which can be passed to Comments::get(). If multiple
  * authors, statuses, or types are specified, we assume an implicit OR
  * such that (e.g.) any author that matches would be returned.
  *
  * @param string $search_string The search string
  * @return array An associative array which can be passed to Comments::get()
  */
 public static function search_to_get($search_string)
 {
     $keywords = array('author' => 1, 'status' => 1, 'type' => 1);
     // Comments::list_comment_statuses and list_comment_types return associative arrays with key/values
     // in the opposite order of the equivalent functions in Posts. Maybe we should change this?
     // In any case, we need to flip them for our purposes
     $statuses = array_flip(Comment::list_comment_statuses());
     $types = array_flip(Comment::list_comment_types());
     $arguments = array('name' => array(), 'status' => array(), 'type' => array());
     $criteria = '';
     $tokens = explode(' ', $search_string);
     foreach ($tokens as $token) {
         // check for a keyword:value pair
         if (preg_match('/^\\w+:\\S+$/u', $token)) {
             list($keyword, $value) = explode(':', $token);
             $keyword = strtolower($keyword);
             $value = MultiByte::strtolower($value);
             switch ($keyword) {
                 case 'author':
                     $arguments['name'][] = $value;
                     break;
                 case 'status':
                     if (isset($statuses[$value])) {
                         $arguments['status'][] = (int) $statuses[$value];
                     }
                     break;
                 case 'type':
                     if (isset($types[$value])) {
                         $arguments['type'][] = (int) $types[$value];
                     }
                     break;
             }
         } else {
             $criteria .= $token . ' ';
         }
     }
     // flatten keys that have single-element or no-element arrays
     foreach ($arguments as $key => $arg) {
         switch (count($arg)) {
             case 0:
                 unset($arguments[$key]);
                 break;
             case 1:
                 $arguments[$key] = $arg[0];
                 break;
         }
     }
     if ($criteria != '') {
         $arguments['criteria'] = $criteria;
     }
     return $arguments;
 }
Example #8
0
    /**
     * Returns a LogEntry or EventLog array based on supplied parameters.
     * By default,fetch as many entries as pagination allows and order them in a descending fashion based on timestamp.
     *
     * @todo Cache query results.
     * @param array $paramarry An associated array of parameters, or a querystring
     * @return array An array of LogEntry objects, or a single LogEntry object, depending on request
     */
    public static function get($paramarray = array())
    {
        $params = array();
        $fns = array('get_results', 'get_row', 'get_value');
        $select = '';
        // Put incoming parameters into the local scope
        $paramarray = Utils::get_params($paramarray);
        $select_fields = LogEntry::default_fields();
        if (!isset($paramarray['return_data'])) {
            unset($select_fields['data']);
        }
        foreach ($select_fields as $field => $value) {
            $select .= '' == $select ? "{log}.{$field}" : ", {log}.{$field}";
        }
        // Default parameters.
        $orderby = 'ORDER BY timestamp DESC, id DESC';
        $limit = Options::get('pagination');
        // Get any full-query parameters
        $possible = array('orderby', 'fetch_fn', 'count', 'month_cts', 'nolimit', 'index', 'limit', 'offset');
        foreach ($possible as $varname) {
            if (isset($paramarray[$varname])) {
                ${$varname} = $paramarray[$varname];
            }
        }
        foreach ($paramarray as $key => $value) {
            if ('orderby' == $key) {
                $orderby = ' ORDER BY ' . $value;
                continue;
            }
        }
        // Transact on possible multiple sets of where information that is to be OR'ed
        if (isset($paramarray['where']) && is_array($paramarray['where'])) {
            $wheresets = $paramarray['where'];
        } else {
            $wheresets = array(array());
        }
        $wheres = array();
        $join = '';
        if (isset($paramarray['where']) && is_string($paramarray['where'])) {
            $wheres[] = $paramarray['where'];
        } else {
            foreach ($wheresets as $paramset) {
                // Safety mechanism to prevent empty queries
                $where = array('1=1');
                $paramset = array_merge((array) $paramarray, (array) $paramset);
                if (isset($paramset['id']) && is_numeric($paramset['id'])) {
                    $where[] = "id= ?";
                    $params[] = $paramset['id'];
                }
                if (isset($paramset['user_id'])) {
                    $where[] = "user_id= ?";
                    $params[] = $paramset['user_id'];
                }
                if (isset($paramset['severity']) && 'any' != LogEntry::severity_name($paramset['severity'])) {
                    $where[] = "severity_id= ?";
                    $params[] = LogEntry::severity($paramset['severity']);
                }
                if (isset($paramset['type_id'])) {
                    if (is_array($paramset['type_id'])) {
                        $types = array_filter($paramset['type_id'], 'is_numeric');
                        if (count($types)) {
                            $where[] = 'type_id IN (' . implode(',', $types) . ')';
                        }
                    } else {
                        $where[] = 'type_id = ?';
                        $params[] = $paramset['type_id'];
                    }
                }
                if (isset($paramset['module'])) {
                    if (!is_array($paramset['module'])) {
                        $paramset['module'] = array($paramset['module']);
                    }
                    $where[] = 'type_id IN ( SELECT DISTINCT id FROM {log_types} WHERE module IN ( ' . implode(', ', array_fill(0, count($paramset['module']), '?')) . ' ) )';
                    $params = array_merge($params, $paramset['module']);
                }
                if (isset($paramset['type'])) {
                    if (!is_array($paramset['type'])) {
                        $paramset['type'] = array($paramset['type']);
                    }
                    $where[] = 'type_id IN ( SELECT DISTINCT id FROM {log_types} WHERE type IN ( ' . implode(', ', array_fill(0, count($paramset['type']), '?')) . ' ) )';
                    $params = array_merge($params, $paramset['type']);
                }
                if (isset($paramset['ip'])) {
                    $where[] = 'ip = ?';
                    $params[] = $paramset['ip'];
                }
                /* do searching */
                if (isset($paramset['criteria'])) {
                    preg_match_all('/(?<=")(\\w[^"]*)(?=")|([:\\w]+)/u', $paramset['criteria'], $matches);
                    foreach ($matches[0] as $word) {
                        if (preg_match('%^id:(\\d+)$%i', $word, $special_crit)) {
                            $where[] .= '(id = ?)';
                            $params[] = $special_crit[1];
                        } else {
                            $where[] .= "( LOWER( message ) LIKE ? )";
                            $params[] = '%' . MultiByte::strtolower($word) . '%';
                        }
                    }
                }
                /**
                 * Build the pubdate
                 * If we've got the day, then get the date.
                 * If we've got the month, but no date, get the month.
                 * If we've only got the year, get the whole year.
                 *
                 * @todo Ensure that we've actually got all the needed parts when we query on them
                 */
                if (isset($paramset['day'])) {
                    $where[] = 'timestamp BETWEEN ? AND ?';
                    $start_date = sprintf('%d-%02d-%02d', $paramset['year'], $paramset['month'], $paramset['day']);
                    $start_date = HabariDateTime::date_create($start_date);
                    $params[] = $start_date->sql;
                    $params[] = $start_date->modify('+1 day')->sql;
                    //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, 0, $paramset['month'], $paramset['day'], $paramset['year'] ) );
                    //$params[] = date( 'Y-m-d H:i:s', mktime( 23, 59, 59, $paramset['month'], $paramset['day'], $paramset['year'] ) );
                } elseif (isset($paramset['month'])) {
                    $where[] = 'timestamp BETWEEN ? AND ?';
                    $start_date = sprintf('%d-%02d-%02d', $paramset['year'], $paramset['month'], 1);
                    $start_date = HabariDateTime::date_create($start_date);
                    $params[] = $start_date->sql;
                    $params[] = $start_date->modify('+1 month')->sql;
                    //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, 0, $paramset['month'], 1, $paramset['year'] ) );
                    //$params[] = date( 'Y-m-d H:i:s', mktime( 23, 59, 59, $paramset['month'] + 1, 0, $paramset['year'] ) );
                } elseif (isset($paramset['year'])) {
                    $where[] = 'timestamp BETWEEN ? AND ?';
                    $start_date = sprintf('%d-%02d-%02d', $paramset['year'], 1, 1);
                    $start_date = HabariDateTime::date_create($start_date);
                    $params[] = $start_date->sql;
                    $params[] = $start_date->modify('+1 year')->sql;
                    //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, 0, 1, 1, $paramset['year'] ) );
                    //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, -1, 1, 1, $paramset['year'] + 1 ) );
                }
                $wheres[] = ' (' . implode(' AND ', $where) . ') ';
            }
        }
        if (isset($index) && is_numeric($index)) {
            $offset = (intval($index) - 1) * intval($limit);
        }
        if (isset($fetch_fn)) {
            if (!in_array($fetch_fn, $fns)) {
                $fetch_fn = $fns[0];
            }
        } else {
            $fetch_fn = $fns[0];
        }
        if (isset($count)) {
            $select = "COUNT({$count})";
            $fetch_fn = 'get_value';
            $orderby = '';
        }
        if (isset($limit)) {
            $limit = " LIMIT {$limit}";
            if (isset($offset)) {
                $limit .= " OFFSET {$offset}";
            }
        }
        // If the month counts are requested, replace the select clause
        if (isset($paramset['month_cts'])) {
            // @todo shouldn't this hand back to habari to convert to DateTime so it reflects the right timezone?
            $select = 'MONTH(FROM_UNIXTIME(timestamp)) AS month, YEAR(FROM_UNIXTIME(timestamp)) AS year, COUNT(*) AS ct';
            $groupby = 'year, month';
            $orderby = ' ORDER BY year, month';
        }
        if (isset($nolimit) || isset($month_cts)) {
            $limit = '';
        }
        $query = '
			SELECT ' . $select . '
			FROM {log} ' . $join;
        if (count($wheres) > 0) {
            $query .= ' WHERE ' . implode(" \nOR\n ", $wheres);
        }
        $query .= !isset($groupby) || $groupby == '' ? '' : ' GROUP BY ' . $groupby;
        $query .= $orderby . $limit;
        // Utils::debug( $paramarray, $fetch_fn, $query, $params );
        DB::set_fetch_mode(PDO::FETCH_CLASS);
        DB::set_fetch_class('LogEntry');
        $results = DB::$fetch_fn($query, $params, 'LogEntry');
        // If the fetch callback function is not get_results,
        // return an EventLog ArrayObject filled with the results as LogEntry objects.
        if ('get_results' != $fetch_fn) {
            return $results;
        } elseif (is_array($results)) {
            $c = __CLASS__;
            $return_value = new $c($results);
            $return_value->get_param_cache = $paramarray;
            return $return_value;
        }
    }
Example #9
0
 /**
  * Either just display the login form; or check a user's credentials, and
  * create a session for them; or handle a password reset request.
  */
 public function act_login()
 {
     // If we're a reset password request, do that.
     if (isset($_POST['submit_button']) && $_POST['submit_button'] === _t('Reset password')) {
         Utils::check_request_method(array('POST'));
         $name = $this->handler_vars['habari_username'];
         if ($name !== NULL) {
             if (!is_numeric($name) && ($user = User::get($name))) {
                 $hash = Utils::random_password();
                 $user->info->password_reset = md5($hash);
                 $user->info->commit();
                 $message = _t('Please visit %1$s to reset your password.', array(URL::get('user', array('page' => 'password_reset', 'id' => $user->id, 'hash' => $hash))));
                 Utils::mail($user->email, _t('[%1$s] Password reset request for %2$s', array(Options::get('title'), $user->displayname)), $message);
             }
             // Moving this inside the check for user existence would allow attackers to test usernames, so don't
             Session::notice(_t('A password reset request has been sent to the user.'));
         }
     } else {
         Utils::check_request_method(array('GET', 'HEAD', 'POST'));
         $name = $_POST['habari_username'];
         $pass = $_POST['habari_password'];
         if (NULL != $name || NULL != $pass) {
             $user = User::authenticate($name, $pass);
             if ($user instanceof User && FALSE != $user) {
                 /* Successfully authenticated. */
                 // Timestamp last login date and time.
                 $user->info->authenticate_time = date('Y-m-d H:i:s');
                 $user->update();
                 // Remove left over expired session error message.
                 if (Session::has_errors('expired_session')) {
                     Session::remove_error('expired_session');
                 }
                 $login_session = Session::get_set('login');
                 if (!empty($login_session)) {
                     /* Now that we know we're dealing with the same user, transfer the form data so he does not lose his request */
                     if (!empty($login_session['post_data'])) {
                         Session::add_to_set('last_form_data', $last_form_data['post'], 'post');
                     }
                     if (!empty($login_session['get_data'])) {
                         Session::add_to_set('last_form_data', $last_form_data['get'], 'get');
                     }
                     /* Redirect to the correct admin page */
                     $dest = explode('/', MultiByte::substr($login_session['original'], MultiByte::strpos($login_session['original'], 'admin/')));
                     if ('' == $dest[0]) {
                         $login_dest = Site::get_url('admin');
                     } else {
                         // Replace '?' with '&' in $dest[1] before call URL::get()
                         // Therefore calling URL::get() with a query string
                         $dest[1] = str_replace('?', '&', $dest[1]);
                         $login_dest = URL::get('admin', 'page=' . $dest[1]);
                     }
                 } else {
                     $login_session = null;
                     $login_dest = Site::get_url('admin');
                 }
                 // filter the destination
                 $login_dest = Plugins::filter('login_redirect_dest', $login_dest, $user, $login_session);
                 // finally, redirect to the destination
                 Utils::redirect($login_dest);
                 return TRUE;
             }
             /* Authentication failed. */
             // Remove submitted password, see, we're secure!
             $_POST['habari_password'] = '';
             $this->handler_vars['error'] = _t('Bad credentials');
         }
     }
     // Display the login form.
     $this->login_form($name);
 }
Example #10
0
 /**
  * Trims longer phrases to shorter ones with elipsis in the middle
  * @param string $str The string to truncate
  * @param integer $len The length of the returned string
  * @param bool $middle Whether to place the ellipsis in the middle (true) or at the end (false)
  * @return string The truncated string
  */
 public static function truncate($str, $len = 10, $middle = true)
 {
     // make sure $len is a positive integer
     if (!is_numeric($len) || 0 > $len) {
         return $str;
     }
     // if the string is less than the length specified, bail out
     if (MultiByte::strlen($str) <= $len) {
         return $str;
     }
     // okay.  Shuold we place the ellipse in the middle?
     if ($middle) {
         // yes, so compute the size of each half of the string
         $len = round(($len - 3) / 2);
         // and place an ellipse in between the pieces
         return MultiByte::substr($str, 0, $len) . '&hellip;' . MultiByte::substr($str, -$len);
     } else {
         // no, the ellipse goes at the end
         $len = $len - 3;
         return MultiByte::substr($str, 0, $len) . '&hellip;';
     }
 }
Example #11
0
 /**
  * Static helper function to quickly fetch an URL, with semantics similar to
  * PHP's file_get_contents. Does not support
  *
  * Returns the content on success or false if an error occurred.
  *
  * @param string $url The URL to fetch
  * @param bool $use_include_path whether to search the PHP include path first (unsupported)
  * @param resource $context a stream context to use (unsupported)
  * @param int $offset how many bytes to skip from the beginning of the result
  * @param int $maxlen how many bytes to return
  * @return string description
  */
 public static function get_contents($url, $use_include_path = false, $context = null, $offset = 0, $maxlen = -1)
 {
     try {
         $rr = new RemoteRequest($url);
         if ($rr->execute() === true) {
             return $maxlen != -1 ? MultiByte::substr($rr->get_response_body(), $offset, $maxlen) : MultiByte::substr($rr->get_response_body(), $offset);
         } else {
             return false;
         }
     } catch (Exception $e) {
         // catch any exceptions to try and emulate file_get_contents() as closely as possible.
         // if you want more control over the errors, instantiate RemoteRequest manually
         return false;
     }
 }
Example #12
0
 /**
  * Receive a Pingback via XMLRPC
  * @param array $params An array of XMLRPC parameters from the remote call
  * @return string The success state of the pingback
  */
 public function xmlrpc_pingback__ping($params)
 {
     try {
         list($source_uri, $target_uri) = $params;
         // This should really be done by an Habari core function
         $target_parse = InputFilter::parse_url($target_uri);
         $target_stub = $target_parse['path'];
         $base_url = Site::get_path('base', TRUE);
         if ('/' != $base_url) {
             $target_stub = str_replace($base_url, '', $target_stub);
         }
         $target_stub = trim($target_stub, '/');
         if (strpos($target_stub, '?') !== FALSE) {
             list($target_stub, $query_string) = explode('?', $target_stub);
         }
         // Can this be used as a target?
         $target_slug = URL::parse($target_stub)->named_arg_values['slug'];
         if ($target_slug === FALSE) {
             throw new XMLRPCException(33);
         }
         // Does the target exist?
         $target_post = Post::get(array('slug' => $target_slug));
         if ($target_post === FALSE) {
             throw new XMLRPCException(32);
         }
         // Is comment allowed?
         if ($target_post->info->comments_disabled) {
             throw new XMLRPCException(33);
         }
         // Is this Pingback already registered?
         if (Comments::get(array('post_id' => $target_post->id, 'url' => $source_uri, 'type' => Comment::PINGBACK))->count() > 0) {
             throw new XMLRPCException(48);
         }
         // Retrieve source contents
         $rr = new RemoteRequest($source_uri);
         $rr->execute();
         if (!$rr->executed()) {
             throw new XMLRPCException(16);
         }
         $source_contents = $rr->get_response_body();
         // encoding is converted into internal encoding.
         // @todo check BOM at beginning of file before checking for a charset attribute
         $habari_encoding = MultiByte::hab_encoding();
         if (preg_match("/<meta[^>]+charset=([A-Za-z0-9\\-\\_]+)/i", $source_contents, $matches) !== FALSE && strtolower($habari_encoding) != strtolower($matches[1])) {
             $ret = MultiByte::convert_encoding($source_contents, $habari_encoding, $matches[1]);
             if ($ret !== FALSE) {
                 $source_contents = $ret;
             }
         }
         // Find the page's title
         preg_match('/<title>(.*)<\\/title>/is', $source_contents, $matches);
         $source_title = $matches[1];
         // Find the reciprocal links and their context
         preg_match('/<body[^>]*>(.+)<\\/body>/is', $source_contents, $matches);
         $source_contents_filtered = preg_replace('/\\s{2,}/is', ' ', strip_tags($matches[1], '<a>'));
         if (!preg_match('%.{0,100}?<a[^>]*?href\\s*=\\s*("|\'|)' . $target_uri . '\\1[^>]*?' . '>(.+?)</a>.{0,100}%s', $source_contents_filtered, $source_excerpt)) {
             throw new XMLRPCException(17);
         }
         /** Sanitize Data */
         $source_excerpt = '...' . InputFilter::filter($source_excerpt[0]) . '...';
         $source_title = InputFilter::filter($source_title);
         $source_uri = InputFilter::filter($source_uri);
         /* Sanitize the URL */
         if (!empty($source_uri)) {
             $parsed = InputFilter::parse_url($source_uri);
             if ($parsed['is_relative']) {
                 // guess if they meant to use an absolute link
                 $parsed = InputFilter::parse_url('http://' . $source_uri);
                 if (!$parsed['is_error']) {
                     $source_uri = InputFilter::glue_url($parsed);
                 } else {
                     // disallow relative URLs
                     $source_uri = '';
                 }
             }
             if ($parsed['is_pseudo'] || $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https') {
                 // allow only http(s) URLs
                 $source_uri = '';
             } else {
                 // reconstruct the URL from the error-tolerant parsing
                 // http:moeffju.net/blog/ -> http://moeffju.net/blog/
                 $source_uri = InputFilter::glue_url($parsed);
             }
         }
         // Add a new pingback comment
         $pingback = new Comment(array('post_id' => $target_post->id, 'name' => $source_title, 'email' => '', 'url' => $source_uri, 'ip' => sprintf("%u", ip2long($_SERVER['REMOTE_ADDR'])), 'content' => $source_excerpt, 'status' => Comment::STATUS_UNAPPROVED, 'date' => HabariDateTime::date_create(), 'type' => Comment::PINGBACK));
         $pingback->insert();
         // Respond to the Pingback
         return 'The pingback has been registered';
     } catch (XMLRPCException $e) {
         $e->output_fault_xml();
     }
 }
Example #13
0
 /**
  * Grabs post data and inserts that data into the internal
  * handler_vars array, which eventually gets extracted into
  * the theme's ( and thereby the template_engine's ) local
  * symbol table for use in the theme's templates
  *
  * This is the default, generic function to grab posts.  To
  * "filter" the posts retrieved, simply pass any filters to
  * the handler_vars variables associated with the post retrieval.
  * For instance, to filter by tag, ensure that handler_vars['tag']
  * contains the tag to filter by.  Simple as that.
  */
 public function act_display($paramarray = array('user_filters' => array()))
 {
     Utils::check_request_method(array('GET', 'HEAD', 'POST'));
     // Get any full-query parameters
     $possible = array('user_filters', 'fallback', 'posts', 'post', 'content_type');
     foreach ($possible as $varname) {
         if (isset($paramarray[$varname])) {
             ${$varname} = $paramarray[$varname];
         }
     }
     /**
      * Since handler_vars no longer contains $_GET and $_POST, we have broken out our valid filters into
      * an array based upon where we should expect them to be. We then only merge those specific pieces together.
      *
      * These are ordered so that handler vars gets overwritten by POST, which gets overwritten by GET, should the
      * same key exist multiple places. This seemed logical to me at the time, but needs further thought.
      */
     $where_filters = array();
     $where_filters_hv = Controller::get_handler_vars()->filter_keys($this->valid_filters['handler_vars']);
     $where_filters_post = $_POST->filter_keys($this->valid_filters['POST']);
     $where_filters_get = $_GET->filter_keys($this->valid_filters['GET']);
     $where_filters = $where_filters_hv->merge($where_filters_post, $where_filters_get);
     $where_filters['vocabulary'] = array();
     if (array_key_exists('tag', $where_filters)) {
         $tags = Tags::parse_url_tags($where_filters['tag']);
         $not_tag = $tags['exclude_tag'];
         $all_tag = $tags['include_tag'];
         if (count($not_tag) > 0) {
             $where_filters['vocabulary'] = array_merge($where_filters['vocabulary'], array(Tags::vocabulary()->name . ':not:term' => $not_tag));
         }
         if (count($all_tag) > 0) {
             $where_filters['vocabulary'] = array_merge($where_filters['vocabulary'], array(Tags::vocabulary()->name . ':all:term' => $all_tag));
         }
         $where_filters['tag_slug'] = Utils::slugify($where_filters['tag']);
         unset($where_filters['tag']);
     }
     if (!isset($_GET['preview'])) {
         $where_filters['status'] = Post::status('published');
     }
     if (!isset($posts)) {
         $user_filters = Plugins::filter('template_user_filters', $user_filters);
         // Work around the tags parameters to Posts::get() being subsumed by the vocabulary parameter
         if (isset($user_filters['not:tag'])) {
             $user_filters['vocabulary'] = array(Tags::vocabulary()->name . ':not:term' => $user_filters['not:tag']);
             unset($user_filters['not:tag']);
         }
         if (isset($user_filters['tag'])) {
             $user_filters['vocabulary'] = array(Tags::vocabulary()->name . ':term_display' => $user_filters['tag']);
             unset($user_filters['tag']);
         }
         $where_filters = $where_filters->merge($user_filters);
         $where_filters = Plugins::filter('template_where_filters', $where_filters);
         $posts = Posts::get($where_filters);
     }
     $this->assign('posts', $posts);
     if ($posts !== false && count($posts) > 0) {
         if (count($posts) == 1) {
             $post = $posts instanceof Post ? $posts : reset($posts);
             Stack::add('body_class', Post::type_name($post->content_type) . '-' . $post->id);
             Stack::add('body_class', 'single');
         } else {
             $post = reset($posts);
             Stack::add('body_class', 'multiple');
         }
         $this->assign('post', $post);
         $type = Post::type_name($post->content_type);
     } elseif ($posts === false || isset($where_filters['page']) && $where_filters['page'] > 1 && count($posts) == 0) {
         if ($this->template_exists('404')) {
             $fallback = array('404');
             // Replace template variables with the 404 rewrite rule
             $this->request->{URL::get_matched_rule()->name} = false;
             $this->request->{URL::set_404()->name} = true;
             $this->matched_rule = URL::get_matched_rule();
             // 404 status header sent in act_display_404, but we're past
             // that, so send it now.
             header('HTTP/1.1 404 Not Found', true, 404);
         } else {
             $this->display('header');
             echo '<h2>';
             _e("Whoops! 404. The page you were trying to access is not really there. Please try again.");
             echo '</h2>';
             header('HTTP/1.1 404 Not Found', true, 404);
             $this->display('footer');
             die;
         }
     }
     $extract = $where_filters->filter_keys('page', 'type', 'id', 'slug', 'posttag', 'year', 'month', 'day', 'tag', 'tag_slug');
     foreach ($extract as $key => $value) {
         ${$key} = $value;
     }
     $this->assign('page', isset($page) ? $page : 1);
     if (!isset($fallback)) {
         // Default fallbacks based on the number of posts
         $fallback = array('{$type}.{$id}', '{$type}.{$slug}', '{$type}.tag.{$posttag}');
         if (count($posts) > 1) {
             $fallback[] = '{$type}.multiple';
             $fallback[] = 'multiple';
         } else {
             $fallback[] = '{$type}.single';
             $fallback[] = 'single';
         }
     }
     $searches = array('{$id}', '{$slug}', '{$year}', '{$month}', '{$day}', '{$type}', '{$tag}');
     $replacements = array(isset($post) && $post instanceof Post ? $post->id : '-', isset($post) && $post instanceof Post ? $post->slug : '-', isset($year) ? $year : '-', isset($month) ? $month : '-', isset($day) ? $day : '-', isset($type) ? $type : '-', isset($tag_slug) ? $tag_slug : '-');
     $fallback[] = 'home';
     $fallback = Plugins::filter('template_fallback', $fallback, $posts, isset($post) ? $post : null);
     $fallback = array_values(array_unique(MultiByte::str_replace($searches, $replacements, $fallback)));
     for ($z = 0; $z < count($fallback); $z++) {
         if (MultiByte::strpos($fallback[$z], '{$posttag}') !== false && isset($post) && $post instanceof Post) {
             $replacements = array();
             if ($alltags = $post->tags) {
                 foreach ($alltags as $current_tag) {
                     $replacements[] = MultiByte::str_replace('{$posttag}', $current_tag->term, $fallback[$z]);
                 }
                 array_splice($fallback, $z, 1, $replacements);
             } else {
                 break;
             }
         }
     }
     return $this->display_fallback($fallback);
 }
Example #14
0
 public function test_detect_encoding()
 {
     $this->assert_equal(MultiByte::detect_encoding('foo'), 'ASCII');
     $str = MultiByte::convert_encoding($this->test_strings['jis'], 'JIS');
     $this->assert_equal(MultiByte::detect_encoding($str), 'JIS');
 }
 private static function save($xml, $type)
 {
     $timestamp = HabariDateTime::date_create('now');
     $result = Cache::set('exportsnapshot__' . $timestamp->int, $xml, 0, true);
     // 0s expiration, but keep it forever
     if ($result) {
         $snapshots = Options::get('exportsnapshot__snapshots', array());
         $snapshots[$timestamp->int] = array('size' => MultiByte::strlen($xml), 'type' => $type, 'ts' => $timestamp->int);
         Options::set('exportsnapshot__snapshots', $snapshots);
         return true;
     } else {
         return false;
     }
 }
Example #16
0
 /**
  * Returns a form for editing this post
  * @param string $context The context the form is being created in, most often 'admin'
  * @return FormUI A form appropriate for creating and updating this post.
  */
 public function get_form($context)
 {
     $form = new FormUI('create-content');
     $form->class[] = 'create';
     $newpost = 0 === $this->id;
     // If the post has already been saved, add a link to its permalink
     if (!$newpost) {
         $post_links = $form->append('wrapper', 'post_links');
         $permalink = $this->status != Post::status('published') ? $this->permalink . '?preview=1' : $this->permalink;
         $post_links->append('static', 'post_permalink', '<a href="' . $permalink . '" class="viewpost" >' . ($this->status != Post::status('published') ? _t('Preview Post') : _t('View Post')) . '</a>');
         $post_links->class = 'container';
     }
     // Store this post instance into a hidden field for later use when saving data
     $form->append('hidden', 'post', $this, _t('Title'), 'admincontrol_text');
     // Create the Title field
     $form->append('text', 'title', 'null:null', _t('Title'), 'admincontrol_text');
     $form->title->class[] = 'important';
     $form->title->class[] = 'check-change';
     $form->title->tabindex = 1;
     $form->title->value = $this->title_internal;
     // Create the silos
     if (count(Plugins::get_by_interface('MediaSilo'))) {
         $form->append('silos', 'silos');
         $form->silos->silos = Media::dir();
     }
     // Create the Content field
     $form->append('textarea', 'content', 'null:null', _t('Content'), 'admincontrol_textarea');
     $form->content->class[] = 'resizable';
     $form->content->class[] = 'check-change';
     $form->content->tabindex = 2;
     $form->content->value = $this->content_internal;
     $form->content->raw = true;
     // Create the tags field
     $form->append('text', 'tags', 'null:null', _t('Tags, separated by, commas'), 'admincontrol_text');
     $form->tags->class = 'check-change';
     $form->tags->tabindex = 3;
     $tags = (array) $this->get_tags();
     array_walk($tags, function (&$element, $key) {
         $element->term_display = MultiByte::strpos($element->term_display, ',') === false ? $element->term_display : $element->tag_text_searchable;
     });
     $form->tags->value = implode(', ', $tags);
     // Create the splitter
     $publish_controls = $form->append('tabs', 'publish_controls');
     // Create the publishing controls
     // pass "false" to list_post_statuses() so that we don't include internal post statuses
     $statuses = Post::list_post_statuses($this);
     unset($statuses[array_search('any', $statuses)]);
     $statuses = Plugins::filter('admin_publish_list_post_statuses', $statuses);
     $settings = $publish_controls->append('fieldset', 'settings', _t('Settings'));
     $settings->append('select', 'status', 'null:null', _t('Content State'), array_flip($statuses), 'tabcontrol_select');
     $settings->status->value = $this->status;
     // hide the minor edit checkbox if the post is new
     if ($newpost) {
         $settings->append('hidden', 'minor_edit', 'null:null');
         $settings->minor_edit->value = false;
     } else {
         $settings->append('checkbox', 'minor_edit', 'null:null', _t('Minor Edit'), 'tabcontrol_checkbox');
         $settings->minor_edit->value = true;
         $form->append('hidden', 'modified', 'null:null')->value = $this->modified;
     }
     $settings->append('checkbox', 'comments_enabled', 'null:null', _t('Comments Allowed'), 'tabcontrol_checkbox');
     $settings->comments_enabled->value = $this->info->comments_disabled ? false : true;
     $settings->append('text', 'pubdate', 'null:null', _t('Publication Time'), 'tabcontrol_text');
     $settings->pubdate->value = $this->pubdate->format('Y-m-d H:i:s');
     $settings->pubdate->helptext = _t('YYYY-MM-DD HH:MM:SS');
     $settings->append('hidden', 'updated', 'null:null');
     $settings->updated->value = $this->updated->int;
     $settings->append('text', 'newslug', 'null:null', _t('Content Address'), 'tabcontrol_text');
     $settings->newslug->id = 'newslug';
     $settings->newslug->value = $this->slug;
     // Create the button area
     $buttons = $form->append('fieldset', 'buttons');
     $buttons->template = 'admincontrol_buttons';
     $buttons->class[] = 'container';
     $buttons->class[] = 'buttons';
     $buttons->class[] = 'publish';
     // Create the Save button
     $require_any = array('own_posts' => 'create', 'post_any' => 'create', 'post_' . Post::type_name($this->content_type) => 'create');
     if ($newpost && User::identify()->can_any($require_any) || !$newpost && ACL::access_check($this->get_access(), 'edit')) {
         $buttons->append('submit', 'save', _t('Save'), 'admincontrol_submit');
         $buttons->save->tabindex = 4;
     }
     // Add required hidden controls
     $form->append('hidden', 'content_type', 'null:null');
     $form->content_type->id = 'content_type';
     $form->content_type->value = $this->content_type;
     $form->append('hidden', 'post_id', 'null:null');
     $form->post_id->id = 'id';
     $form->post_id->value = $this->id;
     $form->append('hidden', 'slug', 'null:null');
     $form->slug->value = $this->slug;
     $form->slug->id = 'originalslug';
     $form->on_success(array($this, 'form_publish_success'));
     // Let plugins alter this form
     Plugins::act('form_publish', $form, $this, $context);
     $content_types = array_flip(Post::list_active_post_types());
     Plugins::act('form_publish_' . Utils::slugify($content_types[$this->content_type], '_'), $form, $this, $context);
     // Return the form object
     return $form;
 }
Example #17
0
	/**
	 * get_dir returns a complete filesystem path to the requested item
	 *	'config_file' returns the complete path to the config.php file, including the filename
	 *	'config' returns the path of the directory containing config.php
	 *	'user' returns the path of the user directory
	 *	'theme' returns the path of the site's active theme
	 * @param string the name of the path item to return
	 * @param bool whether to include a trailing slash.  Default: No
	 * @return string Path
	 */
	public static function get_dir( $name, $trail = false )
	{
		$path = '';

		switch ( strtolower( $name ) ) {
			case 'config_file':
				$path = Site::get_dir( 'config' ) . '/config.php';
				break;
			case 'config':
				if ( self::$config_path ) {
					return self::$config_path;
				}

				self::$config_path = HABARI_PATH;

				$config_dirs = preg_replace( '/^' . preg_quote( HABARI_PATH, '/' ) . '\/user\/sites\/(.*)\/config.php/', '$1', Utils::glob( HABARI_PATH . '/user/sites/*/config.php' ) );

				if ( empty( $config_dirs ) ) {
					return self::$config_path;
				}

				$server = InputFilter::parse_url( Site::get_url( 'habari' ) );
				$server = ( isset( $server['port'] ) ) ? $server['port'] . '.' . $server['host'] . '.' : $server['host'] . '.';

				$request = explode( '/', trim( $_SERVER['REQUEST_URI'], '/' ) );
				$match = trim( $server, '.' );
				$x = count( $request );
				do {
					if ( in_array( $match, $config_dirs ) ) {
						self::$config_dir = $match;
						self::$config_path = HABARI_PATH . '/user/sites/' . self::$config_dir;
						self::$config_type = ( $x > 0 ) ? Site::CONFIG_SUBDOMAIN : Site::CONFIG_SUBDIR;
						break;
					}

					$match = MultiByte::substr( $match, MultiByte::strpos( $match, '.' ) + 1 );
					$x--;
				} while ( MultiByte::strpos( $match, '.' ) !== false );

				$path = self::$config_path;
				break;
			case 'user':
				if ( Site::get_dir( 'config' ) == HABARI_PATH ) {
					$path = HABARI_PATH . '/user';
				}
				else {
					$path = Site::get_dir( 'config' );
				}
				break;
			case 'theme':
				$theme = Themes::get_theme_dir();
				if ( file_exists( Site::get_dir( 'config' ) . '/themes/' . $theme ) ) {
					$path = Site::get_dir( 'user' ) . '/themes/' . $theme;
				}
				elseif ( file_exists( HABARI_PATH . '/user/themes/' . $theme ) ) {
					$path = HABARI_PATH . '/user/themes/' . $theme;
				}
				elseif ( file_exists( HABARI_PATH . '/3rdparty/themes/' . $theme ) ) {
					$url = Site::get_url( 'habari' ) . '/3rdparty/themes/' . $theme;
				}
				else {
					$path = HABARI_PATH . '/system/themes/' . $theme;
				}
				break;
			case 'admin_theme':
				$path = HABARI_PATH . '/system/admin';
				break;
			case 'vendor':
				$path = HABARI_PATH . '/system/vendor';
				break;
		}
		$path .= Utils::trail( $trail );
		$path = Plugins::filter( 'site_dir_' . $name, $path );
		return $path;
	}
Example #18
0
 /**
  * Returns a LogEntry or EventLog array based on supplied parameters.
  * By default,fetch as many entries as pagination allows and order them in a descending fashion based on timestamp.
  *
  * @todo Cache query results.
  * @param array $paramarray An associated array of parameters, or a querystring
  * The following keys are supported:
  * - id => an entry id or array of post ids
  * - user_id => id of the logged in user for which to return entries
  * - severity => severity level for which to return entries
  * - type_id => the numeric id or array of ids for the type of entries for which which to return entries
  * - module => a name or array of names of modules for which to return entries
  * - type => a single type name or array of type names for which to return entries
  * - ip => the IP number for which to return entries
  * - criteria => a literal search string to match entry message content or a special search
  * - day => a day of entry creation, ignored if month and year are not specified
  * - month => a month of entry creation, ignored if year isn't specified
  * - year => a year of entry creation
  * - orderby => how to order the returned entries
  * - fetch_fn => the function used to fetch data, one of 'get_results', 'get_row', 'get_value'
  * - count => return the number of entries that would be returned by this request
  * - month_cts => return the number of entries created in each month
  * - nolimit => do not implicitly set limit
  * - limit => the maximum number of entries to return, implicitly set for many queries
  * - index => 
  * - offset => amount by which to offset returned entries, used in conjunction with limit
  * - where => manipulate the generated WHERE clause
  * - return_data => set to return the data associated with the entry
  * 
  * @return array An array of LogEntry objects, or a single LogEntry object, depending on request
  */
 public static function get($paramarray = array())
 {
     $params = array();
     $fns = array('get_results', 'get_row', 'get_value');
     $select_ary = array();
     $select_distinct = array();
     // Put incoming parameters into the local scope
     $paramarray = Utils::get_params($paramarray);
     if ($paramarray instanceof \ArrayIterator) {
         $paramarray = $paramarray->getArrayCopy();
     }
     $select_fields = LogEntry::default_fields();
     if (!isset($paramarray['return_data'])) {
         unset($select_fields['data']);
     }
     foreach ($select_fields as $field => $value) {
         if (preg_match('/(?:(?P<table>[\\w\\{\\}]+)\\.)?(?P<field>\\w+)(?:(?:\\s+as\\s+)(?P<alias>\\w+))?/i', $field, $fielddata)) {
             if (empty($fielddata['table'])) {
                 $fielddata['table'] = '{log}';
             }
             if (empty($fielddata['alias'])) {
                 $fielddata['alias'] = $fielddata['field'];
             }
         }
         $select_ary[$fielddata['alias']] = "{$fielddata['table']}.{$fielddata['field']} AS {$fielddata['alias']}";
         $select_distinct[$fielddata['alias']] = "{$fielddata['table']}.{$fielddata['field']}";
     }
     // Transact on possible multiple sets of where information that is to be OR'ed
     if (isset($paramarray['where']) && is_array($paramarray['where'])) {
         $wheresets = $paramarray['where'];
     } else {
         $wheresets = array(array());
     }
     $query = Query::create('{log}');
     $query->select($select_ary);
     if (isset($paramarray['where']) && is_string($paramarray['where'])) {
         $query->where()->add($paramarray['where']);
     }
     foreach ($wheresets as $paramset) {
         $where = new QueryWhere();
         $paramset = array_merge((array) $paramarray, (array) $paramset);
         if (isset($paramset['id'])) {
             $where->in('{log}.id', $paramset['id'], 'log_id', 'intval');
         }
         if (isset($paramset['user_id'])) {
             $where->in('{log}.user_id', $paramset['user_id'], 'log_user_id', 'intval');
         }
         if (isset($paramset['severity']) && 'any' != LogEntry::severity_name($paramset['severity'])) {
             $where->in('{log}.severity_id', $paramset['severity'], 'log_severity_id', function ($a) {
                 return LogEntry::severity($a);
             });
         }
         if (isset($paramset['type_id'])) {
             $where->in('{log}.type_id', $paramset['type_id'], 'log_type_id', 'intval');
         }
         if (isset($paramset['module'])) {
             $paramset['module'] = Utils::single_array($paramset['module']);
             $qry = Query::create('{log_types}');
             $qry->select('{log_types}.id')->distinct();
             $qry->where()->in('{log_types}.module', $paramset['module'], 'log_subquery_module');
             $where->in('{log}.type_id', $qry, 'log_module');
         }
         if (isset($paramset['type'])) {
             $paramset['type'] = Utils::single_array($paramset['type']);
             $qry = Query::create('{log_types}');
             $qry->select('{log_types}.id')->distinct();
             $qry->where()->in('{log_types}.type', $paramset['type'], 'log_subquery_type');
             $where->in('{log}.type_id', $qry, 'log_type');
         }
         if (isset($paramset['ip'])) {
             $where->in('{log}.ip', $paramset['ip']);
         }
         /* do searching */
         if (isset($paramset['criteria'])) {
             // this regex matches any unicode letters (\p{L}) or numbers (\p{N}) inside a set of quotes (but strips the quotes) OR not in a set of quotes
             preg_match_all('/(?<=")(\\w[^"]*)(?=")|([:\\w]+)/u', $paramset['criteria'], $matches);
             foreach ($matches[0] as $word) {
                 if (preg_match('%^id:(\\d+)$%i', $word, $special_crit)) {
                     $where->in('{log}.id', $special_crit[1], 'log_special_criteria');
                 } else {
                     $crit_placeholder = $query->new_param_name('criteria');
                     $where->add("( LOWER( {log}.message ) LIKE :{$crit_placeholder}", array($crit_placeholder => '%' . MultiByte::strtolower($word) . '%'));
                 }
             }
         }
         /**
          * Build the pubdate
          * If we've got the day, then get the date.
          * If we've got the month, but no date, get the month.
          * If we've only got the year, get the whole year.
          *
          * @todo Ensure that we've actually got all the needed parts when we query on them
          */
         if (isset($paramset['day']) && isset($paramset['month']) && isset($paramset['year'])) {
             $start_date = sprintf('%d-%02d-%02d', $paramset['year'], $paramset['month'], $paramset['day']);
             $start_date = DateTime::create($start_date);
             $where->add('timestamp BETWEEN :start_date AND :end_date', array('start_date' => $start_date->sql, 'end_date' => $start_date->modify('+1 day -1 second')->sql));
         } elseif (isset($paramset['month']) && isset($paramset['year'])) {
             $start_date = sprintf('%d-%02d-%02d', $paramset['year'], $paramset['month'], 1);
             $start_date = DateTime::create($start_date);
             $where->add('timestamp BETWEEN :start_date AND :end_date', array('start_date' => $start_date->sql, 'end_date' => $start_date->modify('+1 month -1 second')->sql));
         } elseif (isset($paramset['year'])) {
             $start_date = sprintf('%d-%02d-%02d', $paramset['year'], 1, 1);
             $start_date = DateTime::create($start_date);
             $where->add('timestamp BETWEEN :start_date AND :end_date', array('start_date' => $start_date->sql, 'end_date' => $start_date->modify('+1 year -1 second')->sql));
         }
         // Concatenate the WHERE clauses
         $query->where()->add($where);
     }
     // Default parameters.
     $orderby = 'timestamp DESC, id DESC';
     //		$limit = Options::get( 'pagination' );
     // Get any full-query parameters
     $paramarray = new SuperGlobal($paramarray);
     $extract = $paramarray->filter_keys('orderby', 'fetch_fn', 'count', 'month_cts', 'nolimit', 'index', 'limit', 'offset');
     foreach ($extract as $key => $value) {
         ${$key} = $value;
     }
     if (isset($index) && is_numeric($index)) {
         $offset = (intval($index) - 1) * intval($limit);
     }
     if (isset($fetch_fn)) {
         if (!in_array($fetch_fn, $fns)) {
             $fetch_fn = $fns[0];
         }
     } else {
         $fetch_fn = $fns[0];
     }
     if (isset($count)) {
         $query->set_select("COUNT({$count})");
         $fetch_fn = isset($paramarray['fetch_fn']) ? $fetch_fn : 'get_value';
         $orderby = null;
         $groupby = null;
         $having = null;
     }
     // If the month counts are requested, replace the select clause
     if (isset($paramset['month_cts'])) {
         // @todo shouldn't this hand back to habari to convert to DateTime so it reflects the right timezone?
         $query->set_select('MONTH(FROM_UNIXTIME(timestamp)) AS month, YEAR(FROM_UNIXTIME(timestamp)) AS year, COUNT(*) AS ct');
         $groupby = 'year, month';
         if (!isset($paramarray['orderby'])) {
             $orderby = 'year, month';
         }
     }
     if (isset($nolimit) || isset($month_cts)) {
         $limit = null;
     }
     // Define the LIMIT, OFFSET, ORDER BY, GROUP BY if they exist
     if (isset($limit)) {
         $query->limit($limit);
     }
     if (isset($offset)) {
         $query->offset($offset);
     }
     if (isset($orderby)) {
         $query->orderby($orderby);
     }
     if (isset($groupby)) {
         $query->groupby($groupby);
     }
     /*
     if(isset($paramarray['type'])) {
     	print_r($query->params());
     	print_r($query->get());die();
     }
     */
     /* All SQL parts are constructed, on to real business! */
     DB::set_fetch_mode(\PDO::FETCH_CLASS);
     DB::set_fetch_class('LogEntry');
     $results = DB::$fetch_fn($query->get(), $query->params(), 'LogEntry');
     // If the fetch callback function is not get_results,
     // return an EventLog ArrayObject filled with the results as LogEntry objects.
     if ('get_results' != $fetch_fn) {
         return $results;
     } elseif (is_array($results)) {
         $c = __CLASS__;
         $return_value = new $c($results);
         $return_value->get_param_cache = $paramarray;
         return $return_value;
     }
 }
Example #19
0
    /**
     * Returns a post or posts based on supplied parameters.
     * @todo <b>THIS CLASS SHOULD CACHE QUERY RESULTS!</b>
     *
     * @param array $paramarray An associative array of parameters, or a querystring.
     * The following keys are supported:
     * - id => a post id or array of post ids
     * - not:id => a post id or array of post ids to exclude
     * - slug => a post slug or array of post slugs
     * - not:slug => a post slug or array of post slugs to exclude
     * - user_id => an author id or array of author ids
     * - content_type => a post content type or array post content types
     * - not:content_type => a post content type or array post content types to exclude
     * - status => a post status, an array of post statuses, or 'any' for all statuses
     * - year => a year of post publication
     * - month => a month of post publication, ignored if year is not specified
     * - day => a day of post publication, ignored if month and year are not specified
     * - before => a timestamp to compare post publication dates
     * - after => a timestamp to compare post publication dates
     * - month_cts => return the number of posts published in each month
     * - criteria => a literal search string to match post content
     * - title => an exact case-insensitive match to a post title
     * - title_search => a search string that acts only on the post title
     * - has:info => a post info key or array of post info keys, which should be present
     * - all:info => a post info key and value pair or array of post info key and value pairs, which should all be present and match
     * - not:all:info => a post info key and value pair or array of post info key and value pairs, to exclude if all are present and match
     * - any:info => a post info key and value pair or array of post info key and value pairs, any of which can match
     * - not:any:info => a post info key and value pair or array of post info key and value pairs, to exclude if any are present and match
     * - vocabulary => an array describing parameters related to vocabularies attached to posts. This can be one of two forms:
     *   - object-based, in which an array of Term objects are passed
     *     - any => posts associated with any of the terms are returned
     *     - all => posts associated with all of the terms are returned
     *     - not => posts associated with none of the terms are returned
     *   - property-based, in which an array of vocabulary names and associated fields are passed
     *     - vocabulary_name:term => a vocabulary name and term slug pair or array of vocabulary name and term slug pairs, any of which can be associated with the posts
     *     - vocabulary_name:term_display => a vocabulary name and term display pair or array of vocabulary name and term display pairs, any of which can be associated with the posts
     *     - vocabulary_name:not:term => a vocabulary name and term slug pair or array of vocabulary name and term slug pairs, none of which can be associated with the posts
     *     - vocabulary_name:not:term_display => a vocabulary name and term display pair or array of vocabulary name and term display pairs, none of which can be associated with the posts
     *     - vocabulary_name:all:term => a vocabulary name and term slug pair or array of vocabulary name and term slug pairs, all of which must be associated with the posts
     *     - vocabulary_name:all:term_display => a vocabulary name and term display pair or array of vocabulary name and term display pairs, all of which must be associated with the posts
     * - limit => the maximum number of posts to return, implicitly set for many queries
     * - nolimit => do not implicitly set limit
     * - offset => amount by which to offset returned posts, used in conjunction with limit
     * - page => the 'page' of posts to return when paging, sets the appropriate offset
     * - count => return the number of posts that would be returned by this request
     * - orderby => how to order the returned posts
     * - groupby => columns by which to group the returned posts, for aggregate functions
     * - having => for selecting posts based on an aggregate function
     * - where => manipulate the generated WHERE clause. Currently broken, see https://trac.habariproject.org/habari/ticket/1383
     * - add_select => an array of clauses to be added to the generated SELECT clause.
     * - fetch_fn => the function used to fetch data, one of 'get_results', 'get_row', 'get_value', 'get_query'
     *
     * Further description of parameters, including usage examples, can be found at
     * http://wiki.habariproject.org/en/Dev:Retrieving_Posts
     *
     * @return array An array of Post objects, or a single post object, depending on request
     */
    public static function get($paramarray = array())
    {
        // If $paramarray is a querystring, convert it to an array
        $paramarray = Utils::get_params($paramarray);
        // let plugins alter the param array before we use it. could be useful for modifying search results, etc.
        $paramarray = Plugins::filter('posts_get_paramarray', $paramarray);
        $join_params = array();
        $params = array();
        $fns = array('get_results', 'get_row', 'get_value', 'get_query');
        $select_ary = array();
        // Default fields to select, everything by default
        foreach (Post::default_fields() as $field => $value) {
            $select_ary[$field] = "{posts}.{$field} AS {$field}";
            $select_distinct[$field] = "{posts}.{$field}";
        }
        // Default parameters
        $orderby = 'pubdate DESC';
        // Define the WHERE sets to process and OR in the final SQL statement
        if (isset($paramarray['where']) && is_array($paramarray['where'])) {
            $wheresets = $paramarray['where'];
        } else {
            $wheresets = array(array());
        }
        /* Start building the WHERE clauses */
        $wheres = array();
        $joins = array();
        // If the request as a textual WHERE clause, skip the processing of the $wheresets since it's empty
        if (isset($paramarray['where']) && is_string($paramarray['where'])) {
            $wheres[] = $paramarray['where'];
        } else {
            foreach ($wheresets as $paramset) {
                // Safety mechanism to prevent empty queries
                $where = array();
                $paramset = array_merge((array) $paramarray, (array) $paramset);
                // $nots= preg_grep( '%^not:(\w+)$%iu', (array) $paramset );
                if (isset($paramset['id'])) {
                    if (is_array($paramset['id'])) {
                        array_walk($paramset['id'], create_function('&$a,$b', '$a = intval( $a );'));
                        $where[] = "{posts}.id IN (" . implode(',', array_fill(0, count($paramset['id']), '?')) . ")";
                        $params = array_merge($params, $paramset['id']);
                    } else {
                        $where[] = "{posts}.id = ?";
                        $params[] = (int) $paramset['id'];
                    }
                }
                if (isset($paramset['not:id'])) {
                    if (is_array($paramset['not:id'])) {
                        array_walk($paramset['not:id'], create_function('&$a,$b', '$a = intval( $a );'));
                        $where[] = "{posts}.id NOT IN (" . implode(',', array_fill(0, count($paramset['not:id']), '?')) . ")";
                        $params = array_merge($params, $paramset['not:id']);
                    } else {
                        $where[] = "{posts}.id != ?";
                        $params[] = (int) $paramset['not:id'];
                    }
                }
                if (isset($paramset['status']) && $paramset['status'] != 'any' && 0 !== $paramset['status']) {
                    if (is_array($paramset['status'])) {
                        // remove 'any' from the list if we have an array
                        $paramset['status'] = array_diff($paramset['status'], array('any'));
                        array_walk($paramset['status'], create_function('&$a,$b', '$a = Post::status( $a );'));
                        $where[] = "{posts}.status IN (" . implode(',', array_fill(0, count($paramset['status']), '?')) . ")";
                        $params = array_merge($params, $paramset['status']);
                    } else {
                        $where[] = "{posts}.status = ?";
                        $params[] = (int) Post::status($paramset['status']);
                    }
                }
                if (isset($paramset['content_type']) && $paramset['content_type'] != 'any' && 0 !== $paramset['content_type']) {
                    if (is_array($paramset['content_type'])) {
                        // remove 'any' from the list if we have an array
                        $paramset['content_type'] = array_diff($paramset['content_type'], array('any'));
                        array_walk($paramset['content_type'], create_function('&$a,$b', '$a = Post::type( $a );'));
                        $where[] = "{posts}.content_type IN (" . implode(',', array_fill(0, count($paramset['content_type']), '?')) . ")";
                        $params = array_merge($params, $paramset['content_type']);
                    } else {
                        $where[] = "{posts}.content_type = ?";
                        $params[] = (int) Post::type($paramset['content_type']);
                    }
                }
                if (isset($paramset['not:content_type'])) {
                    if (is_array($paramset['not:content_type'])) {
                        array_walk($paramset['not:content_type'], create_function('&$a,$b', '$a = Post::type( $a );'));
                        $where[] = "{posts}.content_type NOT IN (" . implode(',', array_fill(0, count($paramset['not:content_type']), '?')) . ")";
                        $params = array_merge($params, $paramset['not:content_type']);
                    } else {
                        $where[] = "{posts}.content_type != ?";
                        $params[] = (int) Post::type($paramset['not:content_type']);
                    }
                }
                if (isset($paramset['slug'])) {
                    if (is_array($paramset['slug'])) {
                        $where[] = "{posts}.slug IN (" . implode(',', array_fill(0, count($paramset['slug']), '?')) . ")";
                        $params = array_merge($params, $paramset['slug']);
                    } else {
                        $where[] = "{posts}.slug = ?";
                        $params[] = (string) $paramset['slug'];
                    }
                }
                if (isset($paramset['not:slug'])) {
                    if (is_array($paramset['not:slug'])) {
                        $where[] = "{posts}.slug NOT IN (" . implode(',', array_fill(0, count($paramset['not:slug']), '?')) . ")";
                        $params = array_merge($params, $paramset['not:slug']);
                    } else {
                        $where[] = "{posts}.slug != ?";
                        $params[] = (string) $paramset['not:slug'];
                    }
                }
                if (isset($paramset['user_id']) && 0 !== $paramset['user_id']) {
                    if (is_array($paramset['user_id'])) {
                        array_walk($paramset['user_id'], create_function('&$a,$b', '$a = intval( $a );'));
                        $where[] = "{posts}.user_id IN (" . implode(',', array_fill(0, count($paramset['user_id']), '?')) . ")";
                        $params = array_merge($params, $paramset['user_id']);
                    } else {
                        $where[] = "{posts}.user_id = ?";
                        $params[] = (int) $paramset['user_id'];
                    }
                }
                if (isset($paramset['vocabulary'])) {
                    if (is_string($paramset['vocabulary'])) {
                        $paramset['vocabulary'] = Utils::get_params($paramset['vocabulary']);
                    }
                    // parse out the different formats we accept arguments in into a single mutli-dimensional array of goodness
                    $paramset['vocabulary'] = self::vocabulary_params($paramset['vocabulary']);
                    $object_id = Vocabulary::object_type_id('post');
                    $all = array();
                    $any = array();
                    $not = array();
                    if (isset($paramset['vocabulary']['all'])) {
                        $all = $paramset['vocabulary']['all'];
                    }
                    if (isset($paramset['vocabulary']['any'])) {
                        $any = $paramset['vocabulary']['any'];
                    }
                    if (isset($paramset['vocabulary']['not'])) {
                        $not = $paramset['vocabulary']['not'];
                    }
                    foreach ($all as $vocab => $value) {
                        foreach ($value as $field => $terms) {
                            // we only support these fields to search by
                            if (!in_array($field, array('id', 'term', 'term_display'))) {
                                continue;
                            }
                            $joins['term2post_posts'] = ' JOIN {object_terms} ON {posts}.id = {object_terms}.object_id';
                            $joins['terms_term2post'] = ' JOIN {terms} ON {object_terms}.term_id = {terms}.id';
                            $joins['terms_vocabulary'] = ' JOIN {vocabularies} ON {terms}.vocabulary_id = {vocabularies}.id';
                            $where[] = '{vocabularies}.name = ? AND {terms}.' . $field . ' IN ( ' . Utils::placeholder_string($terms) . ' ) AND {object_terms}.object_type_id = ?';
                            $params[] = $vocab;
                            $params = array_merge($params, $terms);
                            $params[] = $object_id;
                        }
                        // this causes no posts to match if combined with 'any' below and should be re-thought... somehow
                        $groupby = implode(',', $select_distinct);
                        $having = 'count(*) = ' . count($terms);
                    }
                    foreach ($any as $vocab => $value) {
                        foreach ($value as $field => $terms) {
                            // we only support these fields to search by
                            if (!in_array($field, array('id', 'term', 'term_display'))) {
                                continue;
                            }
                            $joins['term2post_posts'] = ' JOIN {object_terms} ON {posts}.id = {object_terms}.object_id';
                            $joins['terms_term2post'] = ' JOIN {terms} ON {object_terms}.term_id = {terms}.id';
                            $joins['terms_vocabulary'] = ' JOIN {vocabularies} ON {terms}.vocabulary_id = {vocabularies}.id';
                            $where[] = '{vocabularies}.name = ? AND {terms}.' . $field . ' IN ( ' . Utils::placeholder_string($terms) . ' ) AND {object_terms}.object_type_id = ?';
                            $params[] = $vocab;
                            $params = array_merge($params, $terms);
                            $params[] = $object_id;
                        }
                    }
                    foreach ($not as $vocab => $value) {
                        foreach ($value as $field => $terms) {
                            // we only support these fields to search by
                            if (!in_array($field, array('id', 'term', 'term_display'))) {
                                continue;
                            }
                            $where[] = 'NOT EXISTS ( SELECT 1
								FROM {object_terms} 
								JOIN {terms} ON {terms}.id = {object_terms}.term_id 
								JOIN {vocabularies} ON {terms}.vocabulary_id = {vocabularies}.id  
								WHERE {terms}.' . $field . ' IN (' . Utils::placeholder_string($terms) . ')
								AND {object_terms}.object_id = {posts}.id 
								AND {object_terms}.object_type_id = ? 
								AND {vocabularies}.name = ?
							)';
                            $params = array_merge($params, array_values($terms));
                            $params[] = $object_id;
                            $params[] = $vocab;
                        }
                    }
                }
                if (isset($paramset['criteria'])) {
                    // this regex matches any unicode letters (\p{L}) or numbers (\p{N}) inside a set of quotes (but strips the quotes) OR not in a set of quotes
                    preg_match_all('/(?<=")([\\p{L}\\p{N}]+[^"]*)(?=")|([\\p{L}\\p{N}]+)/u', $paramset['criteria'], $matches);
                    foreach ($matches[0] as $word) {
                        $where[] .= "( LOWER( {posts}.title ) LIKE ? OR  LOWER( {posts}.content ) LIKE ?)";
                        $params[] = '%' . MultiByte::strtolower($word) . '%';
                        $params[] = '%' . MultiByte::strtolower($word) . '%';
                        // Not a typo (there are two ? in the above statement)
                    }
                }
                if (isset($paramset['title'])) {
                    $where[] .= "LOWER( {posts}.title ) LIKE ?";
                    $params[] = MultiByte::strtolower($paramset['title']);
                }
                if (isset($paramset['title_search'])) {
                    // this regex matches any unicode letters (\p{L}) or numbers (\p{N}) inside a set of quotes (but strips the quotes) OR not in a set of quotes
                    preg_match_all('/(?<=")([\\p{L}\\p{N}]+[^"]*)(?=")|([\\p{L}\\p{N}]+)/u', $paramset['title_search'], $matches);
                    foreach ($matches[0] as $word) {
                        $where[] .= " LOWER( {posts}.title ) LIKE ? ";
                        $params[] = '%' . MultiByte::strtolower($word) . '%';
                    }
                }
                if (isset($paramset['all:info']) || isset($paramset['info'])) {
                    // merge the two possibile calls together
                    $infos = array_merge(isset($paramset['all:info']) ? $paramset['all:info'] : array(), isset($paramset['info']) ? $paramset['info'] : array());
                    if (Utils::is_traversable($infos)) {
                        $pi_count = 0;
                        foreach ($infos as $info_key => $info_value) {
                            $pi_count++;
                            $joins['info_' . $info_key] = " LEFT JOIN {postinfo} ipi{$pi_count} ON {posts}.id = ipi{$pi_count}.post_id AND ipi{$pi_count}.name = ? AND ipi{$pi_count}.value = ?";
                            $join_params[] = $info_key;
                            $join_params[] = $info_value;
                            $where[] = "ipi{$pi_count}.name <> ''";
                            $select_ary["info_{$info_key}_value"] = "ipi{$pi_count}.value AS info_{$info_key}_value";
                            $select_distinct["info_{$info_key}_value"] = "info_{$info_key}_value";
                        }
                    }
                }
                if (isset($paramset['any:info'])) {
                    if (Utils::is_traversable($paramset['any:info'])) {
                        $pi_count = 0;
                        $pi_where = array();
                        foreach ($paramset['any:info'] as $info_key => $info_value) {
                            $pi_count++;
                            $join_params[] = $info_key;
                            if (is_array($info_value)) {
                                $joins['any_info_' . $info_key] = " LEFT JOIN {postinfo} aipi{$pi_count} ON {posts}.id = aipi{$pi_count}.post_id AND aipi{$pi_count}.name = ? AND aipi{$pi_count}.value IN (" . Utils::placeholder_string(count($info_value)) . ")";
                                $join_params = array_merge($join_params, $info_value);
                            } else {
                                $joins['any_info_' . $info_key] = " LEFT JOIN {postinfo} aipi{$pi_count} ON {posts}.id = aipi{$pi_count}.post_id AND aipi{$pi_count}.name = ? AND aipi{$pi_count}.value = ?";
                                $join_params[] = $info_value;
                            }
                            $pi_where[] = "aipi{$pi_count}.name <> ''";
                            $select_ary["info_{$info_key}_value"] = "aipi{$pi_count}.value AS info_{$info_key}_value";
                            $select_distinct["info_{$info_key}_value"] = "info_{$info_key}_value";
                        }
                        $where[] = '(' . implode(' OR ', $pi_where) . ')';
                    }
                }
                if (isset($paramset['has:info'])) {
                    $the_ins = array();
                    $has_info = Utils::single_array($paramset['has:info']);
                    $pi_count = 0;
                    $pi_where = array();
                    foreach ($has_info as $info_name) {
                        $pi_count++;
                        $joins['has_info_' . $info_name] = " LEFT JOIN {postinfo} hipi{$pi_count} ON {posts}.id = hipi{$pi_count}.post_id AND hipi{$pi_count}.name = ?";
                        $join_params[] = $info_name;
                        $pi_where[] = "hipi{$pi_count}.name <> ''";
                        $select_ary["info_{$info_name}_value"] = "hipi{$pi_count}.value AS info_{$info_name}_value";
                        $select_distinct["info_{$info_name}_value"] = "info_{$info_name}_value";
                    }
                    $where[] = '(' . implode(' OR ', $pi_where) . ')';
                }
                if (isset($paramset['not:all:info']) || isset($paramset['not:info'])) {
                    // merge the two possible calls together
                    $infos = array_merge(isset($paramset['not:all:info']) ? $paramset['not:all:info'] : array(), isset($paramset['not:info']) ? $paramset['not:info'] : array());
                    if (Utils::is_traversable($infos)) {
                        $the_ins = array();
                        foreach ($infos as $info_key => $info_value) {
                            $the_ins[] = ' ({postinfo}.name = ? AND {postinfo}.value = ? ) ';
                            $params[] = $info_key;
                            $params[] = $info_value;
                        }
                        $where[] = '
							{posts}.id NOT IN (
							SELECT post_id FROM {postinfo}
							WHERE ( ' . implode(' OR ', $the_ins) . ' )
							GROUP BY post_id
							HAVING COUNT(*) = ' . count($infos) . ' )
						';
                        // see that hard-coded number? sqlite wets itself if we use a bound parameter... don't change that
                    }
                }
                if (isset($paramset['not:any:info'])) {
                    if (Utils::is_traversable($paramset['not:any:info'])) {
                        foreach ($paramset['not:any:info'] as $info_key => $info_value) {
                            $the_ins[] = ' ({postinfo}.name = ? AND {postinfo}.value = ? ) ';
                            $params[] = $info_key;
                            $params[] = $info_value;
                        }
                        $where[] = '
							{posts}.id NOT IN (
								SELECT post_id FROM {postinfo}
								WHERE ( ' . implode(' OR ', $the_ins) . ' )
							)
						';
                    }
                }
                /**
                 * Build the statement needed to filter by pubdate:
                 * If we've got the day, then get the date;
                 * If we've got the month, but no date, get the month;
                 * If we've only got the year, get the whole year.
                 */
                if (isset($paramset['day']) && isset($paramset['month']) && isset($paramset['year'])) {
                    $where[] = 'pubdate BETWEEN ? AND ?';
                    $start_date = sprintf('%d-%02d-%02d', $paramset['year'], $paramset['month'], $paramset['day']);
                    $start_date = HabariDateTime::date_create($start_date);
                    $params[] = $start_date->sql;
                    $params[] = $start_date->modify('+1 day')->sql;
                    //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, 0, $paramset['month'], $paramset['day'], $paramset['year'] ) );
                    //$params[] = date( 'Y-m-d H:i:s', mktime( 23, 59, 59, $paramset['month'], $paramset['day'], $paramset['year'] ) );
                } elseif (isset($paramset['month']) && isset($paramset['year'])) {
                    $where[] = 'pubdate BETWEEN ? AND ?';
                    $start_date = sprintf('%d-%02d-%02d', $paramset['year'], $paramset['month'], 1);
                    $start_date = HabariDateTime::date_create($start_date);
                    $params[] = $start_date->sql;
                    $params[] = $start_date->modify('+1 month')->sql;
                    //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, 0, $paramset['month'], 1, $paramset['year'] ) );
                    //$params[] = date( 'Y-m-d H:i:s', mktime( 23, 59, 59, $paramset['month'] + 1, 0, $paramset['year'] ) );
                } elseif (isset($paramset['year'])) {
                    $where[] = 'pubdate BETWEEN ? AND ?';
                    $start_date = sprintf('%d-%02d-%02d', $paramset['year'], 1, 1);
                    $start_date = HabariDateTime::date_create($start_date);
                    $params[] = $start_date->sql;
                    $params[] = $start_date->modify('+1 year')->sql;
                    //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, 0, 1, 1, $paramset['year'] ) );
                    //$params[] = date( 'Y-m-d H:i:s', mktime( 0, 0, -1, 1, 1, $paramset['year'] + 1 ) );
                }
                if (isset($paramset['after'])) {
                    $where[] = 'pubdate > ?';
                    $params[] = HabariDateTime::date_create($paramset['after'])->sql;
                }
                if (isset($paramset['before'])) {
                    $where[] = 'pubdate < ?';
                    $params[] = HabariDateTime::date_create($paramset['before'])->sql;
                }
                // Concatenate the WHERE clauses
                if (count($where) > 0) {
                    $wheres[] = ' (' . implode(' AND ', $where) . ') ';
                }
            }
        }
        // Only show posts to which the current user has permission
        if (isset($paramset['ignore_permissions'])) {
            $master_perm_where = '';
        } else {
            // This set of wheres will be used to generate a list of post_ids that this user can read
            $perm_where = array();
            $perm_where_denied = array();
            $params_where = array();
            $where = array();
            // Get the tokens that this user is granted or denied access to read
            $read_tokens = isset($paramset['read_tokens']) ? $paramset['read_tokens'] : ACL::user_tokens(User::identify(), 'read', true);
            $deny_tokens = isset($paramset['deny_tokens']) ? $paramset['deny_tokens'] : ACL::user_tokens(User::identify(), 'deny', true);
            // If a user can read any post type, let him
            if (User::identify()->can('post_any', 'read')) {
                $perm_where = array('post_any' => '(1=1)');
            } else {
                // If a user can read his own posts, let him
                if (User::identify()->can('own_posts', 'read')) {
                    $perm_where['own_posts_id'] = '{posts}.user_id = ?';
                    $params_where[] = User::identify()->id;
                }
                // If a user can read specific post types, let him
                $permitted_post_types = array();
                foreach (Post::list_active_post_types() as $name => $posttype) {
                    if (User::identify()->can('post_' . Utils::slugify($name), 'read')) {
                        $permitted_post_types[] = $posttype;
                    }
                }
                if (count($permitted_post_types) > 0) {
                    $perm_where[] = '{posts}.content_type IN (' . implode(',', $permitted_post_types) . ')';
                }
                // If a user can read posts with specific tokens, let him
                if (count($read_tokens) > 0) {
                    $joins['post_tokens__allowed'] = ' LEFT JOIN {post_tokens} pt_allowed ON {posts}.id= pt_allowed.post_id AND pt_allowed.token_id IN (' . implode(',', $read_tokens) . ')';
                    $perm_where['perms_join_null'] = 'pt_allowed.post_id IS NOT NULL';
                }
                // If a user has access to read other users' unpublished posts, let him
                if (User::identify()->can('post_unpublished', 'read')) {
                    $perm_where[] = '({posts}.status <> ? AND {posts}.user_id <> ?)';
                    $params_where[] = Post::status('published');
                    $params_where[] = User::identify()->id;
                }
            }
            $params_where_denied = array();
            // If a user is denied access to all posts, do so
            if (User::identify()->cannot('post_any')) {
                $perm_where_denied = array('(1=0)');
            } else {
                // If a user is denied read access to specific post types, deny him
                $denied_post_types = array();
                foreach (Post::list_active_post_types() as $name => $posttype) {
                    if (User::identify()->cannot('post_' . Utils::slugify($name))) {
                        $denied_post_types[] = $posttype;
                    }
                }
                if (count($denied_post_types) > 0) {
                    $perm_where_denied[] = '{posts}.content_type NOT IN (' . implode(',', $denied_post_types) . ')';
                }
                // If a user is denied access to read other users' unpublished posts, deny it
                if (User::identify()->cannot('post_unpublished')) {
                    $perm_where_denied[] = '({posts}.status = ? OR {posts}.user_id = ?)';
                    $params_where_denied[] = Post::status('published');
                    $params_where_denied[] = User::identify()->id;
                }
            }
            // This doesn't work yet because you can't pass these arrays by reference
            Plugins::act('post_get_perm_where', $perm_where, $params_where, $paramarray);
            Plugins::act('post_get_perm_where_denied', $perm_where_denied, $params_where_denied, $paramarray);
            // Set up the merge params
            $merge_params = array($join_params, $params);
            // If there are granted permissions to check, add them to the where clause
            if (count($perm_where) == 0 && !isset($joins['post_tokens__allowed'])) {
                // You have no grants.  You get no posts.
                $where['perms_granted'] = '(1=0)';
            } elseif (count($perm_where) > 0) {
                $where['perms_granted'] = '
					(' . implode(' OR ', $perm_where) . ')
				';
                $merge_params[] = $params_where;
            }
            if (count($deny_tokens) > 0) {
                $joins['post_tokens__denied'] = ' LEFT JOIN {post_tokens} pt_denied ON {posts}.id= pt_denied.post_id AND pt_denied.token_id IN (' . implode(',', $deny_tokens) . ')';
                $perm_where_denied['perms_join_null'] = 'pt_denied.post_id IS NULL';
            }
            // If there are denied permissions to check, add them to the where clause
            if (count($perm_where_denied) > 0) {
                $where['perms_denied'] = '
					(' . implode(' AND ', $perm_where_denied) . ')
				';
                $merge_params[] = $params_where_denied;
            }
            // Merge the params
            $params = call_user_func_array('array_merge', $merge_params);
            // AND the separate permission-related WHERE clauses
            $master_perm_where = implode(' AND ', $where);
        }
        // Extract the remaining parameters which will be used onwards
        // For example: page number, fetch function, limit
        $paramarray = new SuperGlobal($paramarray);
        $extract = $paramarray->filter_keys('page', 'fetch_fn', 'count', 'orderby', 'groupby', 'limit', 'offset', 'nolimit', 'having', 'add_select');
        foreach ($extract as $key => $value) {
            ${$key} = $value;
        }
        // Define the LIMIT if it does not exist, unless specific posts are requested or we're getting the monthly counts
        if (!isset($limit) && !isset($paramset['id']) && !isset($paramset['slug']) && !isset($paramset['month_cts'])) {
            $limit = Options::get('pagination') ? (int) Options::get('pagination') : 5;
        } elseif (!isset($limit)) {
            $selected_posts = 0;
            if (isset($paramset['id'])) {
                $selected_posts += count(Utils::single_array($paramset['id']));
            }
            if (isset($paramset['slug'])) {
                $selected_posts += count(Utils::single_array($paramset['slug']));
            }
            $limit = $selected_posts > 0 ? $selected_posts : '';
        }
        // Calculate the OFFSET based on the page number
        if (isset($page) && is_numeric($page) && !isset($paramset['offset'])) {
            $offset = (intval($page) - 1) * intval($limit);
        }
        /**
         * Determine which fetch function to use:
         * If it is specified, make sure it is valid (based on the $fns array defined at the beginning of this function);
         * Else, use 'get_results' which will return a Posts array of Post objects.
         */
        if (isset($fetch_fn)) {
            if (!in_array($fetch_fn, $fns)) {
                $fetch_fn = $fns[0];
            }
        } else {
            $fetch_fn = $fns[0];
        }
        // If the orderby has a function in it, try to create a select field for it with an alias
        if (strpos($orderby, '(') !== false) {
            $orders = explode(',', $orderby);
            $ob_index = 0;
            foreach ($orders as $key => $order) {
                if (!preg_match('%(?P<field>.+)\\s+(?P<direction>DESC|ASC)%i', $order, $order_matches)) {
                    $order_matches = array('field' => $order, 'direction' => '');
                }
                if (strpos($order_matches['field'], '(') !== false) {
                    $ob_index++;
                    $field = 'orderby' . $ob_index;
                    $select_ary[$field] = "{$order_matches['field']} AS {$field}";
                    $select_distinct[$field] = "{$order_matches['field']} AS {$field}";
                    $orders[$key] = $field . ' ' . $order_matches['direction'];
                }
            }
            $orderby = implode(', ', $orders);
        }
        // Add arbitrary fields to the select clause for sorting and output
        if (isset($add_select)) {
            $select_ary = array_merge($select_ary, $add_select);
        }
        /**
         * Turn the requested fields into a comma-separated SELECT field clause
         */
        $select = implode(', ', $select_ary);
        /**
         * If a count is requested:
         * Replace the current fields to select with a COUNT();
         * Change the fetch function to 'get_value';
         * Remove the ORDER BY since it's useless.
         * Remove the GROUP BY (tag search added it)
         */
        if (isset($count)) {
            $select = "COUNT({$count})";
            $fetch_fn = 'get_value';
            $orderby = '';
            $groupby = '';
            $having = '';
        }
        // If the month counts are requested, replaced the select clause
        if (isset($paramset['month_cts'])) {
            if (isset($paramset['vocabulary'])) {
                $select = 'MONTH(FROM_UNIXTIME(pubdate)) AS month, YEAR(FROM_UNIXTIME(pubdate)) AS year, COUNT(DISTINCT {posts}.id) AS ct';
            } else {
                $select = 'MONTH(FROM_UNIXTIME(pubdate)) AS month, YEAR(FROM_UNIXTIME(pubdate)) AS year, COUNT(*) AS ct';
            }
            $groupby = 'year, month';
            if (!isset($paramarray['orderby'])) {
                $orderby = 'year, month';
            }
        }
        // Remove the LIMIT if 'nolimit'
        // Doing this first should allow OFFSET to work
        if (isset($nolimit)) {
            $limit = '';
        }
        // Define the LIMIT and add the OFFSET if it exists
        if (!empty($limit)) {
            $limit = " LIMIT {$limit}";
            if (isset($offset)) {
                $limit .= " OFFSET {$offset}";
            }
        } else {
            $limit = '';
        }
        /* All SQL parts are constructed, on to real business! */
        /**
         * Build the final SQL statement
         */
        $query = '
			SELECT DISTINCT ' . $select . '
			FROM {posts} ' . "\n " . implode("\n ", $joins) . "\n";
        if (count($wheres) > 0) {
            $query .= ' WHERE (' . implode(" \nOR\n ", $wheres) . ')';
            $query .= $master_perm_where == '' ? '' : ' AND (' . $master_perm_where . ')';
        } elseif ($master_perm_where != '') {
            $query .= ' WHERE (' . $master_perm_where . ')';
        }
        $query .= !isset($groupby) || $groupby == '' ? '' : ' GROUP BY ' . $groupby;
        $query .= !isset($having) || $having == '' ? '' : ' HAVING ' . $having;
        $query .= ($orderby == '' ? '' : ' ORDER BY ' . $orderby) . $limit;
        /**
         * DEBUG: Uncomment the following line to display everything that happens in this function
         */
        //print_R('<pre>'.$query.'</pre>');
        //Utils::debug( $paramarray, $fetch_fn, $query, $params );
        //Session::notice($query);
        if ('get_query' == $fetch_fn) {
            return array($query, $params);
        }
        /**
         * Execute the SQL statement using the PDO extension
         */
        DB::set_fetch_mode(PDO::FETCH_CLASS);
        DB::set_fetch_class('Post');
        $results = DB::$fetch_fn($query, $params, 'Post');
        //Utils::debug( $paramarray, $fetch_fn, $query, $params, $results );
        //var_dump( $query );
        /**
         * Return the results
         */
        if ('get_results' != $fetch_fn) {
            // Since a single result was requested, return a single Post object.
            return $results;
        } elseif (is_array($results)) {
            // With multiple results, return a Posts array of Post objects.
            $c = __CLASS__;
            $return_value = new $c($results);
            $return_value->get_param_cache = $paramarray;
            return $return_value;
        }
    }
Example #20
0
                ?>
"><?php 
                echo $action['label'];
                ?>
</a></li>
				<?php 
            }
            ?>
			<?php 
        }
        ?>
		</ul>
	</div>

	<span class="content" ><?php 
        echo MultiByte::substr(strip_tags($post->content), 0, 250);
        ?>
&hellip;</span>
</div>

<?php 
    }
} else {
    ?>
<div class="message none">
	<p><?php 
    _e('No posts could be found to match the query criteria.');
    ?>
</p>
</div>
<?php 
Example #21
0
    /**
     * The plugin sink for the auth_ajax_wp_import_comments hook.
     * Responds via authenticated ajax to requests for comment importing.
     *
     * @param AjaxHandler $handler The handler that handled the request, contains $_POST info
     */
    public function action_auth_ajax_wp_import_comments($handler)
    {
        $valid_fields = array('db_name', 'db_host', 'db_user', 'db_pass', 'db_prefix', 'commentindex', 'category_import', 'utw_import');
        $inputs = array_intersect_key($_POST->getArrayCopy(), array_flip($valid_fields));
        extract($inputs);
        $wpdb = $this->wp_connect($db_host, $db_name, $db_user, $db_pass, $db_prefix);
        if ($wpdb) {
            if (!DB::in_transaction()) {
                DB::begin_transaction();
            }
            $commentcount = $wpdb->get_value("SELECT count( comment_ID ) FROM {$db_prefix}comments;");
            $min = $commentindex * IMPORT_BATCH + 1;
            $max = min(($commentindex + 1) * IMPORT_BATCH, $commentcount);
            echo "<p>Importing comments {$min}-{$max} of {$commentcount}.</p>";
            $postinfo = DB::table('postinfo');
            $post_info = DB::get_results("SELECT post_id, value FROM {$postinfo} WHERE name= 'wp_id';");
            foreach ($post_info as $info) {
                $post_map[$info->value] = $info->post_id;
            }
            $comments = $wpdb->get_results("\n\t\t\t\tSELECT\n\t\t\t\tcomment_content as content,\n\t\t\t\tcomment_author as name,\n\t\t\t\tcomment_author_email as email,\n\t\t\t\tcomment_author_url as url,\n\t\t\t\tINET_ATON( comment_author_IP ) as ip,\n\t\t\t \tcomment_approved as status,\n\t\t\t\tcomment_date as date,\n\t\t\t\tcomment_type as type,\n\t\t\t\tID as wp_post_id\n\t\t\t\tFROM {$db_prefix}comments\n\t\t\t\tINNER JOIN\n\t\t\t\t{$db_prefix}posts on ( {$db_prefix}posts.ID= {$db_prefix}comments.comment_post_ID )\n\t\t\t\tLIMIT {$min}, " . IMPORT_BATCH, array(), 'Comment');
            foreach ($comments as $comment) {
                switch ($comment->type) {
                    case 'pingback':
                        $comment->type = Comment::PINGBACK;
                        break;
                    case 'trackback':
                        $comment->type = Comment::TRACKBACK;
                        break;
                    default:
                        $comment->type = Comment::COMMENT;
                }
                $comment->content = MultiByte::convert_encoding($comment->content);
                $comment->name = MultiByte::convert_encoding($comment->name);
                $carray = $comment->to_array();
                if ($carray['ip'] == '') {
                    $carray['ip'] = 0;
                }
                switch ($carray['status']) {
                    case '0':
                        $carray['status'] = Comment::STATUS_UNAPPROVED;
                        break;
                    case '1':
                        $carray['status'] = Comment::STATUS_APPROVED;
                        break;
                    case 'spam':
                        $carray['status'] = Comment::STATUS_SPAM;
                        break;
                }
                if (isset($post_map[$carray['wp_post_id']])) {
                    $carray['post_id'] = $post_map[$carray['wp_post_id']];
                    unset($carray['wp_post_id']);
                    $c = new Comment($carray);
                    //Utils::debug( $c );
                    try {
                        $c->insert();
                    } catch (Exception $e) {
                        EventLog::log($e->getMessage(), 'err', null, null, print_r(array($c, $e), 1));
                        Session::error($e->getMessage());
                        $errors = Options::get('import_errors');
                        $errors[] = $e->getMessage();
                        Options::set('import_errors', $errors);
                    }
                }
            }
            if (DB::in_transaction()) {
                DB::commit();
            }
            if ($max < $commentcount) {
                $ajax_url = URL::get('auth_ajax', array('context' => 'wp_import_comments'));
                $commentindex++;
                $vars = Utils::addslashes(array('host' => $db_host, 'name' => $db_name, 'user' => $db_user, 'pass' => $db_pass, 'prefix' => $db_prefix));
                echo <<<WP_IMPORT_AJAX1
\t\t\t\t\t<script type="text/javascript">
\t\t\t\t\t\$( '#import_progress' ).load(
\t\t\t\t\t\t"{$ajax_url}",
\t\t\t\t\t\t{
\t\t\t\t\t\t\tdb_host: "{$vars['host']}",
\t\t\t\t\t\t\tdb_name: "{$vars['name']}",
\t\t\t\t\t\t\tdb_user: "******",
\t\t\t\t\t\t\tdb_pass: "******",
\t\t\t\t\t\t\tdb_prefix: "{$vars['prefix']}",
\t\t\t\t\t\t\tcategory_import: "{$category_import}",
\t\t\t\t\t\t\tutw_import: "{$utw_import}",
\t\t\t\t\t\t\tcommentindex: {$commentindex}
\t\t\t\t\t\t}
\t\t\t\t\t );

\t\t\t\t</script>
WP_IMPORT_AJAX1;
            } else {
                EventLog::log('Import complete from "' . $db_name . '"');
                echo '<p>' . _t('Import is complete.') . '</p>';
                $errors = Options::get('import_errors');
                if (count($errors) > 0) {
                    echo '<p>' . _t('There were errors during import:') . '</p>';
                    echo '<ul>';
                    foreach ($errors as $error) {
                        echo '<li>' . $error . '</li>';
                    }
                    echo '</ul>';
                }
            }
        } else {
            EventLog::log(sprintf(_t('Failed to import from "%s"'), $db_name), 'crit');
            Session::error($e->getMessage());
            echo '<p>' . _t('Failed to connect using the given database connection details.') . '</p>';
        }
    }
Example #22
0
	/**
	 * Returns a user or users based on supplied parameters.
	 * @todo This class should cache query results!
	 *
	 * @param array $paramarray An associated array of parameters, or a querystring
	 * @return array An array of User objects, or a single User object, depending on request
	 */
	public static function get( $paramarray = array() )
	{
		$params = array();
		$fns = array( 'get_results', 'get_row', 'get_value' );
		$select = '';
		// what to select -- by default, everything
		foreach ( User::default_fields() as $field => $value ) {
			$select .= ( '' == $select )
				? "{users}.$field"
				: ", {users}.$field";
		}
		// defaults
		$orderby = 'id ASC';
		$nolimit = true;

		// Put incoming parameters into the local scope
		$paramarray = Utils::get_params( $paramarray );

		// Transact on possible multiple sets of where information that is to be OR'ed
		if ( isset( $paramarray['where'] ) && is_array( $paramarray['where'] ) ) {
			$wheresets = $paramarray['where'];
		}
		else {
			$wheresets = array( array() );
		}

		$wheres = array();
		$join = '';
		if ( isset( $paramarray['where'] ) && is_string( $paramarray['where'] ) ) {
			$wheres[] = $paramarray['where'];
		}
		else {
			foreach ( $wheresets as $paramset ) {
				// safety mechanism to prevent empty queries
				$where = array();
				$paramset = array_merge( (array) $paramarray, (array) $paramset );

				$default_fields = User::default_fields();
				foreach ( User::default_fields() as $field => $scrap ) {
					if ( !isset( $paramset[$field] ) ) {
						continue;
					}
					switch ( $field ) {
						case 'id':
							if ( !is_numeric( $paramset[$field] ) ) {
								continue;
							}
						default:
							$where[] = "{$field} = ?";
							$params[] = $paramset[$field];
					}
				}

				if ( isset( $paramset['info'] ) && is_array( $paramset['info'] ) ) {
					$join .= 'INNER JOIN {userinfo} ON {users}.id = {userinfo}.user_id';
					foreach ( $paramset['info'] as $info_name => $info_value ) {
						$where[] = '{userinfo}.name = ? AND {userinfo}.value = ?';
						$params[] = $info_name;
						$params[] = $info_value;
					}
				}

				if ( isset( $paramset['criteria'] ) ) {
					if ( isset( $paramset['criteria_fields'] ) ) {
						// Support 'criteria_fields' => 'author,ip' rather than 'criteria_fields' => array( 'author', 'ip' )
						if ( !is_array( $paramset['criteria_fields'] ) && is_string( $paramset['criteria_fields'] ) ) {
							$paramset['criteria_fields'] = explode( ',', $paramset['criteria_fields'] );
						}
					}
					else {
						$paramset['criteria_fields'] = array( 'username' );
					}
					$paramset['criteria_fields'] = array_unique( $paramset['criteria_fields'] );

					// this regex matches any unicode letters (\p{L}) or numbers (\p{N}) inside a set of quotes (but strips the quotes) OR not in a set of quotes
					preg_match_all( '/(?<=")([\p{L}\p{N}]+[^"]*)(?=")|([\p{L}\p{N}]+)/u', $paramset['criteria'], $matches );
					$where_search = array();
					foreach ( $matches[0] as $word ) {
						foreach ( $paramset['criteria_fields'] as $criteria_field ) {
							$where_search[] .= "( LOWER( {users}.$criteria_field ) LIKE ? )";
							$params[] = '%' . MultiByte::strtolower( $word ) . '%';
						}
					}
					if ( count( $where_search ) > 0 ) {
						$where[] = '(' . implode( " \nOR\n ", $where_search ).')';
					}
				}

				if ( count( $where ) > 0 ) {
					$wheres[] = ' (' . implode( ' AND ', $where ) . ') ';
				}
			}
		}

		// Get any full-query parameters
		$possible = array( 'fetch_fn', 'count', 'nolimit', 'limit', 'offset' );
		foreach ( $possible as $varname ) {
			if ( isset( $paramarray[$varname] ) ) {
				$$varname = $paramarray[$varname];
			}
		}

		if ( isset( $fetch_fn ) ) {
			if ( ! in_array( $fetch_fn, $fns ) ) {
				$fetch_fn = $fns[0];
			}
		}
		else {
			$fetch_fn = $fns[0];
		}

		// is a count being request?
		if ( isset( $count ) ) {
			$select = "COUNT($count)";
			$fetch_fn = 'get_value';
			$orderby = '';
		}
		if ( isset( $limit ) ) {
			unset( $nolimit );
			$limit = " LIMIT $limit";
			if ( isset( $offset ) ) {
				$limit .= " OFFSET $offset";
			}
		}
		if ( isset( $nolimit ) ) {
			$limit = '';
		}

		$query = '
			SELECT ' . $select
			. ' FROM {users} '
			. $join;

		if ( count( $wheres ) > 0 ) {
			$query .= ' WHERE ' . implode( " \nOR\n ", $wheres );
		}
		$query .= ( ( $orderby == '' ) ? '' : ' ORDER BY ' . $orderby ) . $limit;
		//Utils::debug($paramarray, $fetch_fn, $query, $params);

		DB::set_fetch_mode( PDO::FETCH_CLASS );
		DB::set_fetch_class( 'User' );
		$results = DB::$fetch_fn( $query, $params, 'User' );

		if ( 'get_results' != $fetch_fn ) {
			// return the results
			return $results;
		}
		elseif ( is_array( $results ) ) {
			$c = __CLASS__;
			$return_value = new $c( $results );
			$return_value->get_param_cache = $paramarray;
			return $return_value;
		}
	}
Example #23
0
	/**
	 * Receive a Pingback via XMLRPC
	 * @param array $params An array of XMLRPC parameters from the remote call
	 * @return string The success state of the pingback
	 */
	public function xmlrpc_pingback__ping( $params )
	{
		try {
			list( $source_uri, $target_uri )= $params;

			// This should really be done by an Habari core function
			$target_parse = InputFilter::parse_url( $target_uri );
			$target_stub = $target_parse['path'];
			$base_url = Site::get_path( 'base', true );

			if ( '/' != $base_url) {
				$target_stub = str_replace( $base_url, '', $target_stub );
			}

			$target_stub = trim( $target_stub, '/' );

			if ( strpos( $target_stub, '?' ) !== false ) {
				list( $target_stub, $query_string )= explode( '?', $target_stub );
			}

			// Can this be used as a target?
			$target_slug = URL::parse( $target_stub )->named_arg_values['slug'];

			if ( $target_slug === false ) {
				throw new XMLRPCException( 33 );
			}

			// Does the target exist?
			$target_post = Post::get( array( 'slug' => $target_slug ) );

			if ( $target_post === false ) {
				throw new XMLRPCException( 32 );
			}

			// Is comment allowed?
			if ( $target_post->info->comments_disabled ) {
				throw new XMLRPCException( 33 );
			}

			// Is this Pingback already registered?
			if ( Comments::get( array( 'post_id' => $target_post->id, 'url' => $source_uri, 'type' => Comment::PINGBACK ) )->count() > 0 ) {
				throw new XMLRPCException( 48 );
			}

			// Retrieve source contents
			try {
				$rr = new RemoteRequest( $source_uri );
				$rr->execute();
				if ( ! $rr->executed() ) {
					throw new XMLRPCException( 16 );
				}
				$source_contents = $rr->get_response_body();
				$headers = $rr->get_response_headers();
			}
			catch ( XMLRPCException $e ) {
				// catch our special type of exception and re-throw it
				throw $e;
			}
			catch ( Exception $e ) {
				throw new XMLRPCException( -32300 );
			}

			// Encoding is converted into internal encoding.
			// First, detect the source string's encoding
			$habari_encoding = strtoupper( MultiByte::hab_encoding() );
			$source_encoding = 'Windows-1252';
			// Is the charset in the headers?
			if ( isset( $headers['Content-Type'] ) && strpos( $headers['Content-Type'], 'charset' ) !== false ) {
				// This regex should be changed to meet the HTTP spec at some point
				if ( preg_match("/charset[\x09\x0A\x0C\x0D\x20]*=[\x09\x0A\x0C\x0D\x20]*('?)([A-Za-z0-9\-\_]+)\1/i", $headers['Content-Type'], $matches ) ) {
					$source_encoding = strtoupper( $matches[2] );
				}
			}
			// Can we tell the charset from the stream itself?
			else if ( ( $enc = MultiByte::detect_bom_encoding( $source_contents ) ) !== false ) {
				$source_encoding = $enc;
			}
			// Is the charset in a meta tag?
			else if ( preg_match( "/<meta[^>]+charset[\x09\x0A\x0C\x0D\x20]*=[\x09\x0A\x0C\x0D\x20]*([\"']?)([A-Za-z0-9\-\_]+)\1/i", $source_contents, $matches ) ) {
				$source_encoding = strtoupper( $matches[2] );
				if (in_array($source_encoding, array("UTF-16", "UTF-16BE", "UTF-16LE"))) {
					$source_encoding = "UTF-8";
				}
			}
			// Then, convert the string
			$ret = MultiByte::convert_encoding( $source_contents, $habari_encoding, $source_encoding );
			if ( $ret !== false ) {
				$source_contents = $ret;
			}

			// Find the page's title
			preg_match( '/<title>(.*)<\/title>/is', $source_contents, $matches );
			$source_title = $matches[1];

			// Find the reciprocal links and their context
			preg_match( '/<body[^>]*>(.+)<\/body>/is', $source_contents, $matches );
			$source_contents_filtered = preg_replace( '/\s{2,}/is', ' ', strip_tags( $matches[1], '<a>' ) );

			// Get rid of all the non-recriprocal links
			$ht = new HTMLTokenizer( trim( $source_contents_filtered ) );
			$set = $ht->parse();
			$all_links = $set->slice( 'a', array() );
			$keep_links = $set->slice( 'a', array( 'href' => $target_uri ) );
			$bad_links = array_diff( $all_links, $keep_links );
			foreach( $bad_links as $link ) {
				$link->tokenize_replace( '' );
				$set->replace_slice( $link );
			}
			$source_contents_filtered = (string)$set;

			// Get the excerpt
			if ( !preg_match( '%.{0,100}?<a[^>]*?href\\s*=\\s*("|\'|)' . $target_uri . '\\1[^>]*?'.'>(.+?)</a>.{0,100}%s', $source_contents_filtered, $source_excerpt ) ) {
				throw new XMLRPCException( 17 );
			}

			/** Sanitize Data */
			$source_excerpt = '&hellip;' . InputFilter::filter( $source_excerpt[0] ) . '&hellip;';
			$source_title = InputFilter::filter($source_title);
			$source_uri = InputFilter::filter($source_uri);

			/* Sanitize the URL */
			if (!empty($source_uri)) {
				$parsed = InputFilter::parse_url( $source_uri );
				if ( $parsed['is_relative'] ) {
					// guess if they meant to use an absolute link
					$parsed = InputFilter::parse_url( 'http://' . $source_uri );
					if ( ! $parsed['is_error'] ) {
						$source_uri = InputFilter::glue_url( $parsed );
					}
					else {
						// disallow relative URLs
						$source_uri = '';
					}
				}
				if ( $parsed['is_pseudo'] || ( $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https' ) ) {
					// allow only http(s) URLs
					$source_uri = '';
				}
				else {
					// reconstruct the URL from the error-tolerant parsing
					// http:moeffju.net/blog/ -> http://moeffju.net/blog/
					$source_uri = InputFilter::glue_url( $parsed );
				}
			}

			// Add a new pingback comment
			$pingback = new Comment( array(
				'post_id'	=>	$target_post->id,
				'name'		=>	$source_title,
				'email'		=>	'',
				'url'		=>	$source_uri,
				'ip'		=>	Utils::get_ip(),
				'content'	=>	$source_excerpt,
				'status'	=>	Comment::STATUS_UNAPPROVED,
				'date'		=>	HabariDateTime::date_create(),
				'type' 		=> 	Comment::PINGBACK,
				) );

			$pingback->insert();

			// Respond to the Pingback
			return 'The pingback has been registered';
		}
		catch ( XMLRPCException $e ) {
			$e->output_fault_xml();
		}
	}
Example #24
0
 /**
  * Commit $_SESSION data to the database for this user.
  */
 public static function write()
 {
     if (!isset(self::$session_id)) {
         self::create();
     }
     $remote_address = Utils::get_ip();
     // not always set, even by real browsers
     $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
     $dowrite = self::changed();
     if (isset($_SESSION)) {
         // get the data from the ArrayObject
         $data = $_SESSION;
     } else {
         $dowrite = false;
         $data = array();
     }
     // but let a plugin make the final decision. we may want to ignore search spiders, for instance
     $dowrite = Plugins::filter('session_write', $dowrite, self::$session_id, $data);
     if ($dowrite) {
         // DB::update() checks if the record key exists, and inserts if not
         $record = array('ip' => self::get_subnet($remote_address), 'expires' => DateTime::create()->int + self::$lifetime, 'ua' => MultiByte::substr($user_agent, 0, 255), 'data' => serialize($data));
         DB::update(DB::table('sessions'), $record, array('token' => self::$session_id));
     }
 }
Example #25
0
 /**
  * @todo TODO must build DOM to really properly remove offending elements
  * @todo TODO properly filter URLs
  */
 public static function filter_html_elements($str)
 {
     $tokenizer = new HTMLTokenizer($str);
     // tokenize, baby
     $tokens = $tokenizer->parse();
     // filter token stream
     $filtered = new HTMLTokenSet();
     $stack = array();
     foreach ($tokens as $node) {
         switch ($node['type']) {
             case HTMLTokenizer::NODE_TYPE_TEXT:
                 $node['value'] = html_entity_decode($node['value'], ENT_QUOTES, MultiByte::hab_encoding());
                 break;
             case HTMLTokenizer::NODE_TYPE_ELEMENT_OPEN:
             case HTMLTokenizer::NODE_TYPE_ELEMENT_EMPTY:
                 // is this element allowed at all?
                 if (!in_array(strtolower($node['name']), self::$whitelist_elements)) {
                     if (!in_array(strtolower($node['name']), self::$elements_empty)) {
                         array_push($stack, $node['name']);
                     }
                     //$node = null; //remove the node completely
                     // convert the node to text
                     $node = array('type' => HTMLTokenizer::NODE_TYPE_TEXT, 'name' => '#text', 'value' => HTMLTokenSet::token_to_string($node), 'attrs' => array());
                 } else {
                     // check attributes
                     foreach ($node['attrs'] as $k => $v) {
                         $attr_ok = false;
                         // if the attribute is in the global whitelist and validates
                         if (array_key_exists(strtolower($k), self::$whitelist_attributes['*']) && self::check_attr_value(strtolower($k), $v, self::$whitelist_attributes['*'][strtolower($k)])) {
                             $attr_ok = true;
                         }
                         // if there is a whitelist for this node and this attribute is in that list and it validates
                         if (array_key_exists(strtolower($node['name']), self::$whitelist_attributes) && array_key_exists(strtolower($k), self::$whitelist_attributes[strtolower($node['name'])]) && self::check_attr_value(strtolower($k), $v, self::$whitelist_attributes[strtolower($node['name'])][strtolower($k)])) {
                             $attr_ok = true;
                         }
                         // if it wasn't in one of the whitelists or failed its check, remove it
                         if ($attr_ok != true) {
                             unset($node['attrs'][$k]);
                         }
                     }
                 }
                 break;
             case HTMLTokenizer::NODE_TYPE_ELEMENT_CLOSE:
                 if (!in_array(strtolower($node['name']), self::$whitelist_elements)) {
                     if (strtolower($temp = array_pop($stack)) !== strtolower($node['name'])) {
                         // something weird happened (Luke, use the DOM!)
                         array_push($stack, $temp);
                     }
                     //$node = null;
                     //convert the node to text
                     $node = array('type' => HTMLTokenizer::NODE_TYPE_TEXT, 'name' => '#text', 'value' => HTMLTokenSet::token_to_string($node), 'attrs' => array());
                 }
                 break;
             case HTMLTokenizer::NODE_TYPE_PI:
             case HTMLTokenizer::NODE_TYPE_COMMENT:
             case HTMLTokenizer::NODE_TYPE_CDATA_SECTION:
             case HTMLTokenizer::NODE_TYPE_STATEMENT:
             default:
                 $node = null;
                 break;
         }
         if ($node != null) {
             $filtered[] = $node;
         }
     }
     // rebuild our output string
     return preg_replace('#<([^>\\s]+)(?:\\s+[^>]+)?></\\1>#u', '', (string) $filtered);
 }
Example #26
0
 /**
  * Returns a post or posts based on supplied parameters.
  * @todo <b>THIS CLASS SHOULD CACHE QUERY RESULTS!</b>
  *
  * @param array $paramarray An associative array of parameters, or a querystring.
  * The following keys are supported:
  * - id => a post id or array of post ids
  * - not:id => a post id or array of post ids to exclude
  * - slug => a post slug or array of post slugs
  * - not:slug => a post slug or array of post slugs to exclude
  * - user_id => an author id or array of author ids
  * - content_type => a post content type or array post content types
  * - not:content_type => a post content type or array post content types to exclude
  * - status => a post status, an array of post statuses, or 'any' for all statuses
  * - year => a year of post publication
  * - month => a month of post publication, ignored if year is not specified
  * - day => a day of post publication, ignored if month and year are not specified
  * - before => a timestamp to compare post publication dates
  * - after => a timestamp to compare post publication dates
  * - month_cts => return the number of posts published in each month
  * - criteria => a literal search string to match post content or title
  * - title => an exact case-insensitive match to a post title
  * - title_search => a search string that acts only on the post title
  * - has:info => a post info key or array of post info keys, which should be present
  * - all:info => a post info key and value pair or array of post info key and value pairs, which should all be present and match
  * - not:all:info => a post info key and value pair or array of post info key and value pairs, to exclude if all are present and match
  * - any:info => a post info key and value pair or array of post info key and value pairs, any of which can match
  * - not:any:info => a post info key and value pair or array of post info key and value pairs, to exclude if any are present and match
  * - vocabulary => an array describing parameters related to vocabularies attached to posts. This can be one of two forms:
  *   - object-based, in which an array of Term objects are passed
  *     - any => posts associated with any of the terms are returned
  *     - all => posts associated with all of the terms are returned
  *     - not => posts associated with none of the terms are returned
  *   - property-based, in which an array of vocabulary names and associated fields are passed
  *     - vocabulary_name:term => a vocabulary name and term slug pair or array of vocabulary name and term slug pairs, any of which can be associated with the posts
  *     - vocabulary_name:term_display => a vocabulary name and term display pair or array of vocabulary name and term display pairs, any of which can be associated with the posts
  *     - vocabulary_name:not:term => a vocabulary name and term slug pair or array of vocabulary name and term slug pairs, none of which can be associated with the posts
  *     - vocabulary_name:not:term_display => a vocabulary name and term display pair or array of vocabulary name and term display pairs, none of which can be associated with the posts
  *     - vocabulary_name:all:term => a vocabulary name and term slug pair or array of vocabulary name and term slug pairs, all of which must be associated with the posts
  *     - vocabulary_name:all:term_display => a vocabulary name and term display pair or array of vocabulary name and term display pairs, all of which must be associated with the posts
  * - on_query_built => a closure that accepts a Query as a parameter, allowing a plugin to alter the Query for this request directly
  * - limit => the maximum number of posts to return, implicitly set for many queries
  * - nolimit => do not implicitly set limit
  * - offset => amount by which to offset returned posts, used in conjunction with limit
  * - page => the 'page' of posts to return when paging, sets the appropriate offset
  * - count => return the number of posts that would be returned by this request
  * - orderby => how to order the returned posts
  * - groupby => columns by which to group the returned posts, for aggregate functions
  * - having => for selecting posts based on an aggregate function
  * - where => manipulate the generated WHERE clause. Currently broken, see https://trac.habariproject.org/habari/ticket/1383
  * - add_select => an array of clauses to be added to the generated SELECT clause.
  * - fetch_fn => the function used to fetch data, one of 'get_results', 'get_row', 'get_value', 'get_query'
  *
  * Further description of parameters, including usage examples, can be found at
  * http://wiki.habariproject.org/en/Dev:Retrieving_Posts
  *
  * @return Posts|Post|string An array of Post objects, or a single post object, depending on request
  */
 public static function get($paramarray = array())
 {
     static $presets;
     $select_distinct = array();
     // If $paramarray is a string, use it as a Preset
     if (is_string($paramarray)) {
         $paramarray = array('preset' => $paramarray);
     }
     // If $paramarray is a querystring, convert it to an array
     $paramarray = Utils::get_params($paramarray);
     if ($paramarray instanceof \ArrayIterator) {
         $paramarray = $paramarray->getArrayCopy();
     }
     // If a preset is defined, get the named array and merge it with the provided parameters,
     // allowing the additional $paramarray settings to override the preset
     if (isset($paramarray['preset'])) {
         if (!isset($presets)) {
             $presets = Plugins::filter('posts_get_all_presets', $presets, $paramarray['preset']);
         }
         $paramarray = Posts::merge_presets($paramarray, $presets);
     }
     // let plugins alter the param array before we use it. could be useful for modifying search results, etc.
     $paramarray = Plugins::filter('posts_get_paramarray', $paramarray);
     $join_params = array();
     $params = array();
     $fns = array('get_results', 'get_row', 'get_value', 'get_query');
     $select_ary = array();
     // Default fields to select, everything by default
     $default_fields = Plugins::filter('post_default_fields', Post::default_fields(), $paramarray);
     if (isset($paramarray['default_fields'])) {
         $param_defaults = Utils::single_array($paramarray['default_fields']);
         $default_fields = array_merge($default_fields, $param_defaults);
     }
     foreach ($default_fields as $field => $value) {
         if (preg_match('/(?:(?P<table>[\\w\\{\\}]+)\\.)?(?P<field>\\w+)(?:(?:\\s+as\\s+)(?P<alias>\\w+))?/i', $field, $fielddata)) {
             if (empty($fielddata['table'])) {
                 $fielddata['table'] = '{posts}';
             }
             if (empty($fielddata['alias'])) {
                 $fielddata['alias'] = $fielddata['field'];
             }
         }
         $select_ary[$fielddata['alias']] = "{$fielddata['table']}.{$fielddata['field']} AS {$fielddata['alias']}";
         $select_distinct[$fielddata['alias']] = "{$fielddata['table']}.{$fielddata['field']}";
     }
     // Define the WHERE sets to process and OR in the final SQL statement
     if (isset($paramarray['where']) && is_array($paramarray['where'])) {
         $wheresets = $paramarray['where'];
     } else {
         $wheresets = array(array());
     }
     /* Start building the WHERE clauses */
     $query = Query::create('{posts}');
     $query->select($select_ary);
     // If the request has a textual WHERE clause, add it to the query then continue the processing of the $wheresets
     if (isset($paramarray['where']) && is_string($paramarray['where'])) {
         $query->where()->add($paramarray['where']);
     }
     foreach ($wheresets as $paramset) {
         $where = new QueryWhere();
         $paramset = array_merge((array) $paramarray, (array) $paramset);
         if (isset($paramset['id'])) {
             $where->in('{posts}.id', $paramset['id'], 'posts_id', 'intval');
         }
         if (isset($paramset['not:id'])) {
             $where->in('{posts}.id', $paramset['not:id'], 'posts_not_id', 'intval', false);
         }
         if (isset($paramset['status']) && !self::empty_param($paramset['status'])) {
             $where->in('{posts}.status', $paramset['status'], 'posts_status', function ($a) {
                 return Post::status($a);
             });
         }
         if (isset($paramset['not:status']) && !self::empty_param($paramset['not:status'])) {
             $where->in('{posts}.status', $paramset['not:status'], 'posts_not_status', function ($a) {
                 return Post::status($a);
             }, null, false);
         }
         if (isset($paramset['content_type']) && !self::empty_param($paramset['content_type'])) {
             $where->in('{posts}.content_type', $paramset['content_type'], 'posts_content_type', function ($a) {
                 return Post::type($a);
             });
         }
         if (isset($paramset['not:content_type'])) {
             $where->in('{posts}.content_type', $paramset['not:content_type'], 'posts_not_content_type', function ($a) {
                 return Post::type($a);
             }, false);
         }
         if (isset($paramset['slug'])) {
             $where->in('{posts}.slug', $paramset['slug'], 'posts_slug');
         }
         if (isset($paramset['not:slug'])) {
             $where->in('{posts}.slug', $paramset['not:slug'], 'posts_not_slug', null, false);
         }
         if (isset($paramset['user_id']) && 0 !== $paramset['user_id']) {
             $where->in('{posts}.user_id', $paramset['user_id'], 'posts_user_id', 'intval');
         }
         if (isset($paramset['not:user_id']) && 0 !== $paramset['not:user_id']) {
             $where->in('{posts}.user_id', $paramset['not:user_id'], 'posts_not_user_id', 'intval', false);
         }
         if (isset($paramset['vocabulary'])) {
             if (is_string($paramset['vocabulary'])) {
                 $paramset['vocabulary'] = Utils::get_params($paramset['vocabulary']);
             }
             // parse out the different formats we accept arguments in into a single mutli-dimensional array of goodness
             $paramset['vocabulary'] = self::vocabulary_params($paramset['vocabulary']);
             $object_id = Vocabulary::object_type_id('post');
             if (isset($paramset['vocabulary']['all'])) {
                 $all = $paramset['vocabulary']['all'];
                 foreach ($all as $vocab => $value) {
                     foreach ($value as $field => $terms) {
                         // we only support these fields to search by
                         if (!in_array($field, array('id', 'term', 'term_display'))) {
                             continue;
                         }
                         $join_group = Query::new_param_name('join');
                         $query->join('JOIN {object_terms} ' . $join_group . '_ot ON {posts}.id = ' . $join_group . '_ot.object_id', array(), 'term2post_posts_' . $join_group);
                         $query->join('JOIN {terms} ' . $join_group . '_t ON ' . $join_group . '_ot.term_id = ' . $join_group . '_t.id', array(), 'terms_term2post_' . $join_group);
                         $query->join('JOIN {vocabularies} ' . $join_group . '_v ON ' . $join_group . '_t.vocabulary_id = ' . $join_group . '_v.id', array(), 'terms_vocabulary_' . $join_group);
                         $where->in($join_group . '_v.name', $vocab);
                         $where->in($join_group . "_t.{$field}", $terms);
                         $where->in($join_group . '_ot.object_type_id', $object_id);
                     }
                     // this causes no posts to match if combined with 'any' below and should be re-thought... somehow
                     $groupby = implode(',', $select_distinct);
                     $having = 'count(*) = ' . count($terms);
                     // @todo this seems like it's in the wrong place
                 }
             }
             if (isset($paramset['vocabulary']['any'])) {
                 $any = $paramset['vocabulary']['any'];
                 $orwhere = new QueryWhere('OR');
                 foreach ($any as $vocab => $value) {
                     foreach ($value as $field => $terms) {
                         $andwhere = new QueryWhere();
                         // we only support these fields to search by
                         if (!in_array($field, array('id', 'term', 'term_display'))) {
                             continue;
                         }
                         $join_group = Query::new_param_name('join');
                         $query->join('JOIN {object_terms} ' . $join_group . '_ot ON {posts}.id = ' . $join_group . '_ot.object_id', array(), 'term2post_posts_' . $join_group);
                         $query->join('JOIN {terms} ' . $join_group . '_t ON ' . $join_group . '_ot.term_id = ' . $join_group . '_t.id', array(), 'terms_term2post_' . $join_group);
                         $query->join('JOIN {vocabularies} ' . $join_group . '_v ON ' . $join_group . '_t.vocabulary_id = ' . $join_group . '_v.id', array(), 'terms_vocabulary_' . $join_group);
                         $andwhere->in($join_group . '_v.name', $vocab);
                         $andwhere->in($join_group . "_t.{$field}", $terms);
                         $andwhere->in($join_group . '_ot.object_type_id', $object_id);
                     }
                     $orwhere->add($andwhere);
                     // @todo this seems like it's in the wrong place
                 }
                 $where->add($orwhere);
             }
             if (isset($paramset['vocabulary']['not'])) {
                 $not = $paramset['vocabulary']['not'];
                 foreach ($not as $vocab => $value) {
                     foreach ($value as $field => $terms) {
                         // we only support these fields to search by
                         if (!in_array($field, array('id', 'term', 'term_display'))) {
                             continue;
                         }
                         $subquery_alias = Query::new_param_name('subquery');
                         $subquery = Query::create('{object_terms}')->select('object_id');
                         $subquery->join('JOIN {terms} ON {terms}.id = {object_terms}.term_id');
                         $subquery->join('JOIN {vocabularies} ON {terms}.vocabulary_id = {vocabularies}.id');
                         $subquery->where()->in("{terms}.{$field}", $terms);
                         $subquery->where()->in('{object_terms}.object_type_id', $object_id);
                         $subquery->where()->in('{vocabularies}.name', $vocab);
                         $query->join('LEFT JOIN (' . $subquery->get() . ') ' . $subquery_alias . ' ON ' . $subquery_alias . '.object_id = {posts}.id', $subquery->params(), $subquery_alias);
                         $where->add('COALESCE(' . $subquery_alias . '.object_id, 0) = 0');
                     }
                 }
             }
         }
         if (isset($paramset['criteria'])) {
             // this regex matches any unicode letters (\p{L}) or numbers (\p{N}) inside a set of quotes (but strips the quotes) OR not in a set of quotes
             preg_match_all('/(?<=")([\\p{L}\\p{N}]+[^"]*)(?=")|([\\p{L}\\p{N}]+)/u', $paramset['criteria'], $matches);
             foreach ($matches[0] as $word) {
                 $crit_placeholder = $query->new_param_name('criteria');
                 $where->add("( LOWER( {posts}.title ) LIKE :{$crit_placeholder} OR LOWER( {posts}.content ) LIKE :{$crit_placeholder})", array($crit_placeholder => '%' . MultiByte::strtolower($word) . '%'));
             }
         }
         if (isset($paramset['title'])) {
             $where->add("LOWER( {posts}.title ) LIKE :title_match", array('title_match' => MultiByte::strtolower($paramset['title'])));
         }
         if (isset($paramset['title_search'])) {
             // this regex matches any unicode letters (\p{L}) or numbers (\p{N}) inside a set of quotes (but strips the quotes) OR not in a set of quotes
             preg_match_all('/(?<=")([\\p{L}\\p{N}]+[^"]*)(?=")|([\\p{L}\\p{N}]+)/u', $paramset['title_search'], $matches);
             foreach ($matches[0] as $word) {
                 $crit_placeholder = $query->new_param_name('title_search');
                 $where->add("LOWER( {posts}.title ) LIKE :{$crit_placeholder}", array($crit_placeholder => '%' . MultiByte::strtolower($word) . '%'));
             }
         }
         // Handle field queries on posts and joined tables
         foreach ($select_ary as $field => $aliasing) {
             if (in_array($field, array('id', 'title', 'slug', 'status', 'content_type', 'user_id'))) {
                 // skip fields that we're handling a different way
                 continue;
             }
             if (isset($paramset[$field])) {
                 if (is_callable($paramset[$field])) {
                     $paramset[$field]($where, $paramset);
                 } else {
                     $where->in($field, $paramset[$field], 'posts_field_' . $field);
                 }
             }
         }
         //Done
         if (isset($paramset['all:info']) || isset($paramset['info'])) {
             // merge the two possibile calls together
             $infos = array_merge(isset($paramset['all:info']) ? $paramset['all:info'] : array(), isset($paramset['info']) ? $paramset['info'] : array());
             if (Utils::is_traversable($infos)) {
                 $pi_count = 0;
                 foreach ($infos as $info_key => $info_value) {
                     $pi_count++;
                     $infokey_field = Query::new_param_name('info_key');
                     $infovalue_field = Query::new_param_name('info_value');
                     $query->join("LEFT JOIN {postinfo} ipi{$pi_count} ON {posts}.id = ipi{$pi_count}.post_id AND ipi{$pi_count}.name = :{$infokey_field} AND ipi{$pi_count}.value = :{$infovalue_field}", array($infokey_field => $info_key, $infovalue_field => $info_value), 'all_info_' . $info_key);
                     $where->add("ipi{$pi_count}.name <> ''");
                     $query->select(array("info_{$info_key}_value" => "ipi{$pi_count}.value AS info_{$info_key}_value"));
                     $select_distinct["info_{$info_key}_value"] = "info_{$info_key}_value";
                 }
             }
         }
         //Done
         if (isset($paramset['any:info'])) {
             if (Utils::is_traversable($paramset['any:info'])) {
                 $pi_count = 0;
                 $orwhere = new QueryWhere('OR');
                 foreach ($paramset['any:info'] as $info_key => $info_value) {
                     $pi_count++;
                     if (is_array($info_value)) {
                         $infokey_field = Query::new_param_name('info_key');
                         $inwhere = new QueryWhere('');
                         $inwhere->in("aipi{$pi_count}.value", $info_value);
                         $query->join("LEFT JOIN {postinfo} aipi{$pi_count} ON {posts}.id = aipi{$pi_count}.post_id AND aipi{$pi_count}.name = :{$infokey_field} AND " . $inwhere->get(), array_merge(array($info_key), $inwhere->params()), 'any_info_' . $info_key);
                     } else {
                         $infokey_field = Query::new_param_name('info_key');
                         $infovalue_field = Query::new_param_name('info_value');
                         $query->join("LEFT JOIN {postinfo} aipi{$pi_count} ON {posts}.id = aipi{$pi_count}.post_id AND aipi{$pi_count}.name = :{$infokey_field} AND aipi{$pi_count}.value = :{$infovalue_field}", array($infokey_field => $info_key, $infovalue_field => $info_value), 'any_info_' . $info_key);
                     }
                     $orwhere->add("aipi{$pi_count}.name <> ''");
                     $query->select(array("info_{$info_key}_value" => "aipi{$pi_count}.value AS info_{$info_key}_value"));
                     $select_distinct["info_{$info_key}_value"] = "info_{$info_key}_value";
                 }
                 $where->add('(' . $orwhere->get() . ')');
             }
         }
         // Done
         if (isset($paramset['has:info'])) {
             $has_info = Utils::single_array($paramset['has:info']);
             $pi_count = 0;
             $orwhere = new QueryWhere('OR');
             foreach ($has_info as $info_name) {
                 $infoname_field = Query::new_param_name('info_name');
                 $pi_count++;
                 $query->join("LEFT JOIN {postinfo} hipi{$pi_count} ON {posts}.id = hipi{$pi_count}.post_id AND hipi{$pi_count}.name = :{$infoname_field}", array($infoname_field => $info_name), 'has_info_' . $info_name);
                 $orwhere->add("hipi{$pi_count}.name <> ''");
                 $query->select(array("info_{$info_name}_value" => "hipi{$pi_count}.value AS info_{$info_name}_value"));
                 $select_distinct["info_{$info_name}_value"] = "info_{$info_name}_value";
             }
             $where->add('(' . $orwhere->get() . ')');
         }
         //Done
         if (isset($paramset['not:all:info']) || isset($paramset['not:info'])) {
             // merge the two possible calls together
             $infos = array_merge(isset($paramset['not:all:info']) ? $paramset['not:all:info'] : array(), isset($paramset['not:info']) ? $paramset['not:info'] : array());
             if (Utils::is_traversable($infos)) {
                 $orwhere = new QueryWhere('OR');
                 foreach ($infos as $info_key => $info_value) {
                     $andwhere = new QueryWhere();
                     $andwhere->in('{postinfo}.name', $info_key);
                     $andwhere->in('{postinfo}.value', $info_value);
                     $orwhere->add($andwhere);
                 }
                 // see that hard-coded number in having()? sqlite wets itself if we use a bound parameter... don't change that
                 $subquery = Query::create('{postinfo}')->select('{postinfo}.post_id')->groupby('post_id')->having('COUNT(*) = ' . count($infos));
                 $subquery->where()->add($orwhere);
                 $where->in('{posts}.id', $subquery, 'posts_not_all_info_query', null, false);
             }
         }
         //Tested. Test fails with original code
         if (isset($paramset['not:any:info'])) {
             if (Utils::is_traversable($paramset['not:any:info'])) {
                 $subquery = Query::create('{postinfo}')->select('post_id');
                 foreach ($paramset['not:any:info'] as $info_key => $info_value) {
                     $infokey_field = $query->new_param_name('info_key');
                     $infovalue_field = $query->new_param_name('info_value');
                     //							$subquery->where()->add(" ({postinfo}.name = :{$infokey_field} AND {postinfo}.value = :{$infovalue_field} ) ", array($infokey_field => $info_key, $infovalue_field => $info_value));
                     $subquery->where('OR')->add(" ({postinfo}.name = :{$infokey_field} AND {postinfo}.value = :{$infovalue_field} ) ", array($infokey_field => $info_key, $infovalue_field => $info_value));
                 }
                 $where->in('{posts}.id', $subquery, 'posts_not_any_info', null, false);
             }
         }
         /**
          * Build the statement needed to filter by pubdate:
          * If we've got the day, then get the date;
          * If we've got the month, but no date, get the month;
          * If we've only got the year, get the whole year.
          */
         if (isset($paramset['day']) && isset($paramset['month']) && isset($paramset['year'])) {
             $start_date = sprintf('%d-%02d-%02d', $paramset['year'], $paramset['month'], $paramset['day']);
             $start_date = DateTime::create($start_date);
             $where->add('pubdate BETWEEN :start_date AND :end_date', array('start_date' => $start_date->sql, 'end_date' => $start_date->modify('+1 day -1 second')->sql));
         } elseif (isset($paramset['month']) && isset($paramset['year'])) {
             $start_date = sprintf('%d-%02d-%02d', $paramset['year'], $paramset['month'], 1);
             $start_date = DateTime::create($start_date);
             $where->add('pubdate BETWEEN :start_date AND :end_date', array('start_date' => $start_date->sql, 'end_date' => $start_date->modify('+1 month -1 second')->sql));
         } elseif (isset($paramset['year'])) {
             $start_date = sprintf('%d-%02d-%02d', $paramset['year'], 1, 1);
             $start_date = DateTime::create($start_date);
             $where->add('pubdate BETWEEN :start_date AND :end_date', array('start_date' => $start_date->sql, 'end_date' => $start_date->modify('+1 year -1 second')->sql));
         }
         if (isset($paramset['after'])) {
             $where->add('pubdate > :after_date', array('after_date' => DateTime::create($paramset['after'])->sql));
         }
         if (isset($paramset['before'])) {
             $where->add('pubdate < :before_date', array('before_date' => DateTime::create($paramset['before'])->sql));
         }
         // Concatenate the WHERE clauses
         $query->where()->add($where);
     }
     if (isset($paramset['post_join'])) {
         $post_joins = Utils::single_array($paramset['post_join']);
         foreach ($post_joins as $post_join) {
             if (preg_match('#^(\\S+)(?:\\s+as)?\\s+(\\S+)$#i', $post_join, $matches)) {
                 $query->join("LEFT JOIN {$matches[1]} {$matches[2]} ON {$matches[2]}.post_id = {posts}.id ");
             } else {
                 $query->join("LEFT JOIN {$post_join} ON {$post_join}.post_id = {posts}.id ");
             }
         }
     }
     // Only show posts to which the current user has permission
     if (isset($paramset['ignore_permissions'])) {
         $master_perm_where = new QueryWhere();
         // Set up the merge params
         $merge_params = array($join_params, $params);
         $params = call_user_func_array('array_merge', $merge_params);
     } else {
         $master_perm_where = new QueryWhere();
         // This set of wheres will be used to generate a list of post_ids that this user can read
         $perm_where = new QueryWhere('OR');
         $perm_where_denied = new QueryWhere('AND');
         // Get the tokens that this user is granted or denied access to read
         $read_tokens = isset($paramset['read_tokens']) ? $paramset['read_tokens'] : ACL::user_tokens(User::identify(), 'read', true);
         $deny_tokens = isset($paramset['deny_tokens']) ? $paramset['deny_tokens'] : ACL::user_tokens(User::identify(), 'deny', true);
         // If a user can read any post type, let him
         if (User::identify()->can('post_any', 'read')) {
             $perm_where->add('(1=1)');
         } else {
             // If a user can read his own posts, let him
             if (User::identify()->can('own_posts', 'read')) {
                 $perm_where->add('{posts}.user_id = :current_user_id', array('current_user_id' => User::identify()->id));
             }
             // If a user can read specific post types, let him
             $permitted_post_types = array();
             foreach (Post::list_active_post_types() as $name => $posttype) {
                 if (User::identify()->can('post_' . Utils::slugify($name), 'read')) {
                     $permitted_post_types[] = $posttype;
                 }
             }
             if (count($permitted_post_types) > 0) {
                 $perm_where->in('{posts}.content_type', $permitted_post_types, 'posts_permitted_types', 'intval');
             }
             // If a user can read posts with specific tokens, let him
             if (count($read_tokens) > 0) {
                 $query->join('LEFT JOIN {post_tokens} pt_allowed ON {posts}.id= pt_allowed.post_id AND pt_allowed.token_id IN (' . implode(',', $read_tokens) . ')', array(), 'post_tokens__allowed');
                 $perm_where->add('pt_allowed.post_id IS NOT NULL', array(), 'perms_join_not_null');
             }
             // If a user has access to read other users' unpublished posts, let him
             if (User::identify()->can('post_unpublished', 'read')) {
                 $perm_where->add('({posts}.status <> :status_published AND {posts}.user_id <> :current_user_id)', array('current_user_id' => User::identify()->id, 'status_published' => Post::status('published')));
             }
         }
         // If a user is denied access to all posts, do so
         if (User::identify()->cannot('post_any')) {
             $perm_where_denied->add('(1=0)');
         } else {
             // If a user is denied read access to specific post types, deny him
             $denied_post_types = array();
             foreach (Post::list_active_post_types() as $name => $posttype) {
                 if (User::identify()->cannot('post_' . Utils::slugify($name))) {
                     $denied_post_types[] = $posttype;
                 }
             }
             if (count($denied_post_types) > 0) {
                 $perm_where_denied->in('{posts}.content_type', $denied_post_types, 'posts_denied_types', 'intval', false);
             }
             // If a user is denied read access to posts with specific tokens, deny it
             if (count($deny_tokens) > 0) {
                 $query->join('LEFT JOIN {post_tokens} pt_denied ON {posts}.id= pt_denied.post_id AND pt_denied.token_id IN (' . implode(',', $deny_tokens) . ')', array(), 'post_tokens__denied');
                 $perm_where_denied->add('pt_denied.post_id IS NULL', array(), 'perms_join_null');
             }
             // If a user is denied access to read other users' unpublished posts, deny it
             if (User::identify()->cannot('post_unpublished')) {
                 $perm_where_denied->add('({posts}.status = :status_published OR {posts}.user_id = :current_user_id)', array('current_user_id' => User::identify()->id, 'status_published' => Post::status('published')));
             }
         }
         Plugins::act('post_get_perm_where', $perm_where, $paramarray);
         Plugins::act('post_get_perm_where_denied', $perm_where_denied, $paramarray);
         // If there are granted permissions to check, add them to the where clause
         if ($perm_where->count() == 0 && !$query->joined('post_tokens__allowed')) {
             $master_perm_where->add('(1=0)', array(), 'perms_granted');
         } else {
             $master_perm_where->add($perm_where, array(), 'perms_granted');
         }
         // If there are denied permissions to check, add them to the where clause
         if ($perm_where_denied->count() > 0 || $query->joined('post_tokens__denied')) {
             $master_perm_where->add($perm_where_denied, array(), 'perms_denied');
         }
     }
     $query->where()->add($master_perm_where, array(), 'master_perm_where');
     // Extract the remaining parameters which will be used onwards
     // For example: page number, fetch function, limit
     $paramarray = new SuperGlobal($paramarray);
     $extract = $paramarray->filter_keys('page', 'fetch_fn', 'count', 'orderby', 'groupby', 'limit', 'offset', 'nolimit', 'having', 'add_select');
     foreach ($extract as $key => $value) {
         ${$key} = $value;
     }
     // Calculate the OFFSET based on the page number. Requires a limit.
     if (isset($page) && is_numeric($page) && !isset($paramset['offset']) && isset($limit)) {
         $offset = (intval($page) - 1) * intval($limit);
     }
     /**
      * Determine which fetch function to use:
      * If it is specified, make sure it is valid (based on the $fns array defined at the beginning of this function);
      * Else, use 'get_results' which will return a Posts array of Post objects.
      */
     if (isset($fetch_fn)) {
         if (!in_array($fetch_fn, $fns)) {
             $fetch_fn = $fns[0];
         }
     } else {
         $fetch_fn = $fns[0];
     }
     // Add arbitrary fields to the select clause for sorting and output
     if (isset($add_select)) {
         $query->select($add_select);
     }
     /**
      * If a count is requested:
      * Replace the current fields to select with a COUNT();
      * Change the fetch function to 'get_value';
      * Remove the ORDER BY since it's useless.
      * Remove the GROUP BY (tag search added it)
      */
     if (isset($count)) {
         $query->set_select("COUNT({$count})");
         $fetch_fn = isset($paramarray['fetch_fn']) ? $fetch_fn : 'get_value';
         $orderby = null;
         $groupby = null;
         $having = null;
     }
     // If the month counts are requested, replaced the select clause
     if (isset($paramset['month_cts'])) {
         if (isset($paramset['vocabulary'])) {
             $query->set_select('MONTH(FROM_UNIXTIME(pubdate)) AS month, YEAR(FROM_UNIXTIME(pubdate)) AS year, COUNT(DISTINCT {posts}.id) AS ct');
         } else {
             $query->set_select('MONTH(FROM_UNIXTIME(pubdate)) AS month, YEAR(FROM_UNIXTIME(pubdate)) AS year, COUNT(*) AS ct');
         }
         $groupby = 'year, month';
         if (!isset($paramarray['orderby'])) {
             $orderby = 'year, month';
         }
     }
     // Remove the LIMIT if 'nolimit'
     // Doing this first should allow OFFSET to work
     if (isset($nolimit)) {
         $limit = null;
     }
     // Define the LIMIT, OFFSET, ORDER BY, GROUP BY if they exist
     if (isset($limit)) {
         $query->limit($limit);
     }
     if (isset($offset)) {
         $query->offset($offset);
     }
     if (isset($orderby)) {
         $query->orderby($orderby);
     }
     if (isset($groupby)) {
         $query->groupby($groupby);
     }
     if (isset($having)) {
         $query->having($having);
     }
     if (isset($paramarray['on_query_built'])) {
         foreach (Utils::single_array($paramarray['on_query_built']) as $built) {
             $built($query);
         }
     }
     Plugins::act('posts_get_query', $query, $paramarray);
     /* All SQL parts are constructed, on to real business! */
     /**
      * DEBUG: Uncomment the following line to display everything that happens in this function
      */
     //print_R('<pre>'.$query.'</pre>');
     //Utils::debug( $paramarray, $fetch_fn, $query, $params );
     //Session::notice($query);
     if ('get_query' == $fetch_fn) {
         return array($query->get(), $query->params());
     }
     /**
      * Execute the SQL statement using the PDO extension
      */
     DB::set_fetch_mode(\PDO::FETCH_CLASS);
     $fetch_class = 'Post';
     if (isset($paramarray['fetch_class'])) {
         $fetch_class = $paramarray['fetch_class'];
     }
     DB::set_fetch_class($fetch_class);
     $results = DB::$fetch_fn($query->get(), $query->params(), $fetch_class);
     //Utils::debug($results, $query->get(), $query->params());
     //Utils::debug( $paramarray, $fetch_fn, $query->get(), $query->params(), $results );
     //var_dump( $query );
     /**
      * Return the results
      */
     if ('get_results' != $fetch_fn) {
         // Since a single result was requested, return a single Post object.
         return $results;
     } elseif (is_array($results)) {
         // With multiple results, return a Posts array of Post objects.
         $c = __CLASS__;
         $return_value = new $c($results);
         $return_value->get_param_cache = $paramarray;
         return $return_value;
     }
 }
Example #27
0
	/**
	 * returns the integer value of the specified post type, or false
	 * @param mixed a post type name or number
	 * @return mixed an integer or boolean false
	 */
	public static function type( $name )
	{
		$types = Post::list_active_post_types();
		if ( is_numeric( $name ) && ( false !== in_array( $name, $types ) ) ) {
			return $name;
		}
		if ( isset( $types[ MultiByte::strtolower( $name ) ] ) ) {
			return $types[ MultiByte::strtolower( $name ) ];
		}
		return false;
	}
Example #28
0
 /**
  * Parse tag parameters from a URL string
  *
  * @param String $tags The URL parameter string
  *
  * @return Array. Associative array of included and excluded tags
  */
 public static function parse_url_tags($tags, $objectify = false)
 {
     $tags = explode(' ', $tags);
     $exclude_tag = array();
     $include_tag = array();
     foreach ($tags as $tag) {
         if (MultiByte::substr($tag, 0, 1) == '-') {
             $tag = MultiByte::substr($tag, 1);
             $exclude_tag[] = $objectify ? Tags::get_one(Utils::slugify($tag)) : Utils::slugify($tag);
         } else {
             $include_tag[] = $objectify ? Tags::get_one(Utils::slugify($tag)) : Utils::slugify($tag);
         }
     }
     return compact('include_tag', 'exclude_tag');
 }
Example #29
0
</p>
			<?php 
        }
        ?>

			<?php 
        Plugins::act('comment_info', $comment);
        ?>

			<p class="comment-type"><?php 
        echo Plugins::filter('comment_type_display', $comment->typename, 'singular');
        ?>
</p>
		</div>
		<span class="content pct75"><?php 
        if (MultiByte::valid_data($comment->content)) {
            echo nl2br(Utils::htmlspecialchars($comment->content));
        } else {
            _e('this post contains text in an invalid encoding');
        }
        ?>
</span>
	</div>
</div>

<?php 
    }
} else {
    ?>
<div class="message none">
	<p><?php 
Example #30
0
 /**
  * Handles AJAX requests from media silos.
  */
 public function ajax_media($handler_vars)
 {
     Utils::check_request_method(array('POST'));
     $path = $handler_vars['path'];
     $rpath = $path;
     $silo = Media::get_silo($rpath, true);
     // get_silo sets $rpath by reference to the path inside the silo
     $assets = Media::dir($path);
     $output = array('ok' => 1, 'dirs' => array(), 'files' => array(), 'path' => $path);
     foreach ($assets as $asset) {
         if ($asset->is_dir) {
             $output['dirs'][$asset->basename] = $asset->get_props();
         } else {
             $output['files'][$asset->basename] = $asset->get_props();
         }
     }
     $rootpath = MultiByte::strpos($path, '/') !== false ? MultiByte::substr($path, 0, MultiByte::strpos($path, '/')) : $path;
     $controls = array('root' => '<a href="#" onclick="habari.media.fullReload();habari.media.showdir(\'' . $rootpath . '\');return false;">' . _t('Root') . '</a>');
     $controls = Plugins::filter('media_controls', $controls, $silo, $rpath, '');
     $controls_out = '';
     foreach ($controls as $k => $v) {
         if (is_numeric($k)) {
             $controls_out .= "<li>{$v}</li>";
         } else {
             $controls_out .= "<li class=\"{$k}\">{$v}</li>";
         }
     }
     $output['controls'] = $controls_out;
     echo json_encode($output);
 }