Jump to content
Invision Community


IPS Staff
  • Content count

  • Joined

  • Last visited

  • Days Won


bfarber last won the day on September 14

bfarber had the most liked content!

About bfarber

  • Rank
  • Birthday 03/07/1983

Contact Methods

IPS Marketplace

  • Resources Contributor
    Total file submissions: 9

Profile Information

  • Gender
  • Location
    Southwest VA
  • Interests
    This is my interests, just for "Rikki" :)

Recent Profile Visitors

751,262 profile views
  1. Invision Community 4.2

    Please submit a ticket if you require support or have questions about the new features and how they work.
  2. Invision Community 4.2

    If you are experiencing an issue you can submit a ticket in your client area.
  3. Is this a custom application? If so you can just override the canEdit() method in your content item class to set the check however you need.
  4. Wiki based Dev Docs

    We've not made any changes yet
  5. Request Data

    The Invision Community software includes a class to work with request data, including GET, POST, and REQUEST data, cookies, and detecting certain information about the request (such as whether it was submitted via AJAX or not). You will need to use this class to perform some common actions working with web-based software. The class is accessed through \IPS\Requst::i() and implements the Singleton pattern. GET, POST and REQUEST data To access request variables, you simply call them as properties of the class. For instance, you can check if a request variable is set and output it like so if( isset( \IPS\Request::i()->someVariable ) ) { print \IPS\Request::i()->someVariable; } Request data is largely unmodified from the originally submitted data except that NULL bytes and RTL control characters are stripped from the input, and slashes are stripped if magic quotes is enabled. This means that all request data should be considered potentially tainted and you will need to take precautions not to introduce security issues by relying upon "clean" request data from this class. If the request is made using the PUT method (which happens through our REST API in some cases, for instance), this request data is also available through this class. Working with cookies Cookie values are available in the cookies property of the \IPS\Request class. print \IPS\Request::i()->cookie['member_id']; If your site uses a cookie prefix, note that it will be stripped automatically here. To set a cookie, you can use the setCookie method. As with fetching a cookie, you should not include any cookie prefix that is used. The method signature is /** * Set a cookie * * @param string $name Name * @param mixed $value Value * @param \IPS\DateTime|null $expire Expiration date, or NULL for on session end * @param bool $httpOnly When TRUE the cookie will be made accessible only through the HTTP protocol * @param string|null $domain Domain to set to. If NULL, will be detected automatically. * @param string|null $path Path to set to. If NULL, will be detected automatically. * @return bool */ public function setCookie( $name, $value, $expire=NULL, $httpOnly=TRUE, $domain=NULL, $path=NULL ) Typically you should leave $domain and $path as NULL, however these can be overridden if needed, for instance if you are working on integrating with a third party service. You can clear all login cookies through \IPS\Request::i()->clearLoginCookies() if needed, which includes the member_id and pass_hash cookies, as well as any forum password cookies (where a user may have entered a password to access a forum). Other helper methods There are several helper methods in the \IPS\Request class that you can leverage as needed to check various properties of the request \IPS\Request::i()->isAjax() Returns a boolean true or false to indicate if the request was made via AJAX \IPS\Request::i()->isSecure() Returns a boolean true or false to indicate if the request was made over SSL (https) \IPS\Request::i()->url() Returns an \IPS\Http\Url object representing the requested URL. Note that fragments (values after the hash symbol in a URL) are not sent to the server and will not be available to check at the server level. \IPS\Request::i()->ipAddress() Returns the IP address used to make the current request, taking into account proxy servers and forwarding if the administrator has chosen to do so in the AdminCP. \IPS\Request::i()->ipAddressIsBanned() Returns a boolean true or false to denote if the current request IP address is banned in the AdminCP IP address ban filters. \IPS\Request::i()->requestMethod() Returns the current request method, upper-cased. \IPS\Request::i()->isCgi() Returns a boolean true or false to indicate if the current request is being processed by a CGI wrapper in PHP \IPS\Request::i()->floodCheck() Checks the user's search flood control setting to determine if the user has recently searched and is searching again too quickly. If the user has searched recently and this method is called before the flood control time period has completed, an error will be shown to the user, otherwise their "last searched" time is updated to the current time for subsequent checks. \IPS\Request::i()->confirmedDelete( $title, $message, $submit ) When a user deletes data, it is prudent to confirm that the action was intended and not a misclick or similar. To facilitate this you can call the confirmedDelete() method which will verify if the user has confirmed the deletion already, and if not will show a confirmation screen first.
  6. URLs

    The Invision Community framework provides a powerful helper class to work with URLs, including parsing URLs and making requests to (and reading responses from) URLs. The software automatically uses cURL when available, falling back to standard sockets if not (this behavior can also be overridden by constants if an environment wishes to force cURL or sockets, specifically). Working with URLs The \IPS\Http\Url class is used to work with URLs, and two helper methods form the primary interface to create a URL object with this class: internal(): Use this method when creating an internal URL, such as a URL to a forum or topic. The method handles automatically creating friendly URLs when configured to do so. external(): Pass any full URL to this method to create a URL object out of the URL string. // Create a URL to topic ID 1 $url = \IPS\Http\Url::internal( 'app=forums&module=forums&controller=topic&id=1', 'front', 'forums_topic', array( 'topic-friendly-slug' ) ); $url = \IPS\Http\Url::external( 'https://www.google.com' ); // Create a URL object for a standard URL string The internal method accepts the following parameters: The URL query string The 'base' ('front' or 'admin', depending upon whether the URL is to a page in the AdminCP or not) The SEO template, if applicable An array of SEO slugs if the template calls for one A PROTOCOL_* constant from \IPS\Http\Url to override whether an http or https URL should be generated. You can usually omit this parameter and let the software determine the right protocol to use automatically. The external method simply accepts a URL string. If you are unsure if the URL object you are creating is internal or external, you can alternatively use the createFromString() method (passing the full URL as you would with the external() method), however note that this method is somewhat performance-intensive and the directly internal() and external() methods are preferred. The first parameter for this method is the URL, the second parameter is a boolean flag indicating if the URL may be a friendly internal URL, and the third and final parameter is a boolean flag indicating if you wish to automatically encode any components that are invalid instead of throwing an error (defaulting to FALSE - set this to TRUE if the URL is user-supplied and should not throw an error). You can make adjustments to the URL after creating the URL object by calling various methods. setScheme(): You can pass the scheme in (i.e. http or https), or pass in NULL to use a protocol-relative scheme (i.e. no scheme) setHost(): Accepts a full host name setPath(): Accepts a full valid path setQueryString(): Accepts a query string key as the first parameter and its value as the second parameter OR an array of key => value pairs as the first parameter setFragment(): Accepts a fragment stripQueryString(): Accepts a query string key or an array of query string keys and removes those query string parameters (if present) from the URL When you wish to output the URL, you can cast the \IPS\Http\Url object as a string print (string) $urlObject; The class has a few additional properties and methods you may wish to reference or call, outlined below: isInternal: This property denotes if the URL is internal or not isFriendly: This property denotes if the URL is a friendly (internal) URL or not queryString: This property is an array of key => value query string parameters in the URL hiddenQueryString: This property is an array of key => value query string parameters that would have been present if the URL were not a friendly URL. For instance, if you create an internal friendly URL object, this property will contain the associated query string parameters that are not shown (because a friendly URL is used) csrf(): Call this method to add the current viewing user's CSRF key to the URL as a query string argument. This is then later checked in controllers to prevent CSRF-style attacks. If you have a state-changing request that does not use the \IPS\Helpers\Form class, you should typically check the CSRF key. (static) seoTitle(): You can call this method to create a valid friendly URL slug. Typically this is done when content is submitted and the URL slug is stored for later reference, however you can also call this method on the fly if needed. Making requests Once you have created an \IPS\Http\Url object, you can make requests to it. To do this, you first call request() against the URL object. /** * Make a HTTP Request * * @param int|null $timeout Timeout * @param string $httpVersion HTTP Version * @param bool|int $followRedirects Automatically follow redirects? If a number is provided, will follow up to that number of redirects * @return \IPS\Http\Request */ public function request( $timeout=null, $httpVersion=null, $followRedirects=5 ) This returns an \IPS\Http\Request object. From here there are some methods you can call before executing the request: login(): Accepts a username as the first parameter and a password as the second parameter, and performs a basic authorization request against the URL. setHeaders(): Accepts an array of key => value pairs of headers that should be included with the request. sslCheck(): Accepts a boolean true or false as the only parameter, signaling whether SSL certificates should be validated or not. In most cases, this should be left default (true), unless you are aware that the SSL URL you are making a request against has an invalid certificate. forceTls(): Forces TLS for the request. This is primarily used with some payment gateways that enforce TLS requests. Afterwards, you can now make the request. To do so, you call the request method that you wish to perform (e.g. to perform a GET request you call get() and to perform a PUT request you call put()), passing along any parameters that should be included with the request (for POST and PUT requests). $request = \IPS\Http\Url::external( "http://someurl.com" )->request()->get(); This returns an \IPS\Http\Response object, which you can now inspect and manipulate as needed. Firstly, there are several useful properties you may need to reference: httpResponseVersion: This is the HTTP version of the response (1.0 or 1.1, typically) httpResponseCode: This is the HTTP response code. You may need to verify that a valid response code (i.e. 200) was returned after making a request. httpResponseText: This is the HTTP response text. For a 200 request, for instance, this will be "OK". httpHeaders: This will be an array containing all HTTP headers in the response as key => value pairs cookies: This will be an array of all Set-Cookie headers as key => value pairs. content: This is the body of the response. Casting the response as a string returns the content property noted above. There are some methods in the \IPS\Http\Response class that you can leverage to make working with certain common responses easier. decodeJson(): Calling this method will run the response through json_decode before returning it. If the response is not valid JSON, a RuntimeException will be thrown. decodeXml(): Calling this method will parse the response as XML, throwing a RuntimeException if the response is not valid XML. It is additionally worth noting that if the request fails for some reason (for instance, a time out connecting to the remote server), an \IPS\Http\Request\Exception exception will be thrown. To that end, you must wrap requests in a try/catch block. // Create a URL object $url = \IPS\Http\Url::external( "http://someurl.com" )->setQueryString( 'key', 'value' ); // Now fetch it and decode the JSON try { $response = $url->request()->get()->decodeJson(); } catch( \IPS\Http\Request\Exception $e ) { die( "There was a problem fetching the request" ); } catch( \RuntimeException $e ) { die( "The response was not valid JSON" ); } var_dump( $response ); exit;
  7. Accessing the database

    Accessing the database to store and query data is a necessity of nearly all applications and plugins that integrate with the Invision Community software. The \IPS\Db class handles database connections, and extends the default mysqli class in the PHP core library. Connecting to the database The default database connection (denoted by the connection details in conf_global.php) can be established automatically by calling \IPS\Db::i(). If a connection has not yet been established, it will be done so immediately on the fly when this method is called. The default connection uses utf8 (or utf8mb4 depending upon your configuration) and all database tables and columns must be configured as utf8. This is generally handled automatically by the Invision Community software, but is an important note to keep in mind. If you need to establish a connection to a remote database, this can be done by passing parameters to the i() method of \IPS\Db. The first parameter is an arbitrary string connection identifier, and the second parameter is an array with the connection settings. $connection = \IPS\Db::i( 'external', array( 'sql_host' => 'localhost', 'sql_user' => 'username', 'sql_pass' => 'password', 'sql_database' => 'database_name', 'sql_port' => 3306, 'sql_socket' => '/var/lib/mysql.sock', 'sql_utf8mb4' => true, ) ); You only need to supply the parameters that your connection requires. You can also support read/write separation automatically by passing the same parameters a second time with the prefix "sql_read_" instead of just "sql_", pointing to your read-only MySQL instance. Selecting data Selecting data from the database is one of the more common needs when interacting with the database. /** * Build SELECT statement * * @param array|string $columns The columns (as an array) to select or an expression * @param array|string $table The table to select from. Either (string) table_name or (array) ( name, alias ) or \IPS\Db\Select object * @param array|string|NULL $where WHERE clause - see \IPS\Db::compileWhereClause() for details * @param string|NULL $order ORDER BY clause * @param array|int $limit Rows to fetch or array( offset, limit ) * @param string|NULL|array $group Column(s) to GROUP BY * @param array|string|NULL $having HAVING clause (same format as WHERE clause) * @param int $flags Bitwise flags * @li \IPS\Db::SELECT_DISTINCT Will use SELECT DISTINCT * @li \IPS\Db::SELECT_SQL_CALC_FOUND_ROWS Will add SQL_CALC_FOUND_ROWS * @li \IPS\Db::SELECT_MULTIDIMENSIONAL_JOINS Will return the result as a multidimensional array, with each joined table separately * @li \IPS\Db::SELECT_FROM_WRITE_SERVER Will send the query to the write server (if read/write separation is enabled) * @return \IPS\Db\Select * */ public function select( $columns=NULL, $table, $where=NULL, $order=NULL, $limit=NULL, $group=NULL, $having=NULL, $flags=0 ) You can call the select() method to perform a SELECT database query. Doing so returns an \IPS\Db\Select object which allows you to further refine the SELECT query. For instance, there are methods in this class to force a specific index to be used, to join other database tables, and to specify which key/value fields to use for results. // Get the select object $select = \IPS\Db::i()->select( '*', 'some_table', array( 'field=?', 1 ), 'some_column DESC', array( 0, 10 ) ); // Force a specific index to be used for the query $select = $select->forceIndex( 'some_index' ); // Join another table on $select = $select->join( 'other_table_name', 'other_table_name.column=some_table.column', 'LEFT' ); // Now, get the number of results returned $results = count( $select ); // Tell the iterator that keys should be 'column_a' and values should be 'column_b' $select = $select->setKeyField( 'column_a' )->setValueField( 'column_b' ); // Finally, loop over the results foreach( $select as $columnA => $columnB ) { print $columnA . ': ' . $columnB . '<br>'; } There are some important things to note here. The WHERE clause accepts many different formats you should be aware of. You can pass a string as the WHERE clause some_column='some value' or you can pass an array with the first element the WHERE clause using ? as placeholders for values and then each placeholder replaced with the subsequent array entries. This uses prepared statements in MySQL to help avoid SQL injection concerns and is recommended. array( 'some_column=? OR some_column=?', 'first value', 'second value' ) or, finally, you can pass an array of clauses which will be AND joined together array( array( 'some_column=?', 'test value' ), array( 'other_column=?', 1 ) ) You can call setKeyField() without calling setValueField(). Instead of the value being a string in this case, it will simply be an array of all columns selected (or if only one column is selected, the value will be a string with that column's value). Note that you must select the columns you wish to use for setKeyField and/or setValueField. The method definition for join() requires the first parameter to be the name of the table you wish to join, then the join 'on' clause, followed by the type of join to use (defaulting to LEFT). You can also specify a using clause for the join as the fourth parameter, if necessary. To control which columns to select, you will need to adjust the first parameter of the original SELECT clause (if you pass '*' MySQL will return all columns from all tables selected from and/or joined). \IPS\Db\Select implements both Iterator and Countable. This means you can treat it as an array and use a foreach() loop to loop over the results, and you can call count() against the object to get the number of results. Be aware, however, that count() by default only returns the number of results that the query returned. If you have 1000 rows in the table and use a limit clause to only return 100, then count() will show 100. If you wish to be able to retrieve the total number of rows in the table as if no limit clause was used, you can pass a \IPS\Db::SELECT_SQL_CALC_FOUND_ROWS flag to the original select() method as noted by the phpdoc block above. Often you are selecting just one row (i.e. when performing a COUNT(*) query) and a bulit in first() method facilitates this. Be aware, however, that if the row does not exist an UnderflowException is thrown, so you should wrap such queries in a try/catch statement. try { $row = \IPS\Db::i()->select( '*', 'table', array( 'id=?', 2 ) )->first(); } catch( \UnderflowException $e ) { // There is no row with id=2 in table } Inserting, Updating and Deleting rows You will also want to insert, update and delete rows in MySQL periodically. To do so, you use the aptly named insert(), update() and delete() methods of the database driver. // Insert a row $new_id = \IPS\Db::i()->insert( 'some_table', array( 'column_one' => 'value', 'column_two' => 'value2' ) ); // Update that row \IPS\Db::i()->update( 'some_table', array( 'column_two' => 'other value' ), array( 'id_column=?', $new_id ) ); \IPS\Db::i()->delete( 'some_table', array( 'id_column=?', $new_id ) ); // Delete the row Inserting is straight forward. The first parameter to the method is the table name, followed by an associative array of column names => values. The new autoincrement id (if applicable) is returned by the method. Note that there is also a replace() method which behaves like insert(), except a REPLACE INTO query will be executed instead of an INSERT INTO query (in which case, if a duplicate unique index is encountered the original will be replaced with the new row). The update() method expects the first parameter to be the table name, the second parameter to be an associative array of column names => values, and the third parameter to be the where clause (if needed). Additionally, you can pass an array of table joins as the fourth parameter if needed, an array to represent the limit clause as the fifth parameter, and flags to modify the query as the last parameter, including: \IPS\Db::LOW_PRIORITY Will use LOW_PRIORITY \IPS\Db::IGNORE Will use IGNORE The delete() method is typically called with just the first parameter, the table name, to empty out the entire table, or also with a second parameter, the where clause, to delete specific rows. The method additionally accepts a third parameter to control the order of results for the DELETE query, a fourth parameter to limit the number of results deleted, and a fifth column that specifies the statement column if the WHERE clause is a statement. Affecting database structure You can create database tables, add, alter and remove columns from existing tables, and add, alter and remove indexes from existing tables through the \IPS\Db library. Additionally, methods exist to help determine if a table, column or index exists before using it. /** * Does table exist? * * @param string $name Table Name * @return bool */ public function checkForTable( $name ) /** * Does column exist? * * @param string $name Table Name * @param string $column Column Name * @return bool */ public function checkForColumn( $name, $column ) /** * Does index exist? * * @param string $name Table Name * @param string $index Index Name * @return bool */ public function checkForIndex( $name, $index ) /** * Create Table * * @code \IPS\Db::createTable( array( 'name' => 'table_name', // Table name 'columns' => array( ... ), // Column data - see \IPS\Db::compileColumnDefinition for details 'indexes' => array( ... ), // (Optional) Index data - see \IPS\Db::compileIndexDefinition for details 'comment' => '...', // (Optional) Table comment 'engine' => 'MEMORY', // (Optional) Engine to use - will default to not specifying one, unless a FULLTEXT index is specified, in which case MyISAM is forced 'temporary' => TRUE, // (Optional) Will sepcify CREATE TEMPORARY TABLE - defaults to FALSE 'if_not_exists' => TRUE, // (Optional) Will sepcify CREATE TABLE name IF NOT EXISTS - defaults to FALSE ) ); * @endcode * @param array $data Table Definition (see code sample for details) * @throws \IPS\Db\Exception * @return void|string */ public function createTable( $data ) /** * Create copy of table structure * * @param string $table The table name * @param string $newTableName Name of table to create * @throws \IPS\Db\Exception * @return void|string */ public function duplicateTableStructure( $table, $newTableName ) /** * Rename table * * @see <a href='http://dev.mysql.com/doc/refman/5.1/en/rename-table.html'>Rename Table</a> * @param string $oldName The current table name * @param string $newName The new name * @return void * @see <a href='http://stackoverflow.com/questions/12856783/best-practice-with-mysql-innodb-to-rename-huge-table-when-table-with-same-name-a'>Renaming huge InnoDB tables</a> * @see <a href='http://www.percona.com/blog/2011/02/03/performance-problem-with-innodb-and-drop-table/'>Performance problem dropping huge InnoDB tables</a> * @note A race condition can occur sometimes with InnoDB + innodb_file_per_table so we can't drop then rename...see above links */ public function renameTable( $oldName, $newName ) /** * Alter Table * Can only update the comment and engine * @note This will not examine key lengths and adjust. * * @param string $table Table name * @param string|null $comment Table comment. NULL to not change * @param string|null $engine Engine to use. NULL to not change * @return void */ public function alterTable( $table, $comment=NULL, $engine=NULL ) /** * Drop table * * @see <a href='http://dev.mysql.com/doc/refman/5.1/en/drop-table.html'>DROP TABLE Syntax</a> * @param string|array $table Table Name(s) * @param bool $ifExists Adds an "IF EXISTS" clause to the query * @param bool $temporary Table is temporary? * @return mixed */ public function dropTable( $table, $ifExists=FALSE, $temporary=FALSE ) /** * Add column to table in database * * @see \IPS\Db::compileColumnDefinition * @param string $table Table name * @param array $definition Column Definition (see \IPS\Db::compileColumnDefinition for details) * @return void */ public function addColumn( $table, $definition ) /** * Modify an existing column * * @see \IPS\Db::compileColumnDefinition * @param string $table Table name * @param string $column Column name * @param array $definition New column definition (see \IPS\Db::compileColumnDefinition for details) * @return void */ public function changeColumn( $table, $column, $definition ) /** * Drop a column * * @param string $table Table name * @param string|array $column Column name * @return void */ public function dropColumn( $table, $column ) /** * Add index to table in database * * @see \IPS\Db::compileIndexDefinition * @param string $table Table name * @param array $definition Index Definition (see \IPS\Db::compileIndexDefinition for details) * @param bool $discardDuplicates If adding a unique index, should duplicates be discarded? (If FALSE and there are any, an exception will be thrown) * @return void */ public function addIndex( $table, $definition, $discardDuplicates=TRUE ) /** * Modify an existing index * * @see \IPS\Db::compileIndexDefinition * @param string $table Table name * @param string $index Index name * @param array $definition New index definition (see \IPS\Db::compileIndexDefinition for details) * @return void */ public function changeIndex( $table, $index, $definition ) /** * Drop an index * * @param string $table Table name * @param string|array $index Column name * @return mixed */ public function dropIndex( $table, $index ) Most of these methods are self-explanatory and infrequently used, except when using the developer center to add queries for upgrades. Miscellaneous Finally, there are a handful of methods and properties in the class you may find useful or relevant while working with the database driver. If you need to obtain the database prefix being used for tables (represented by sql_tbl_prefix in conf_global) you can do so by calling \IPS\Db::i()->prefix . If you are building queries to manually run, you will need to prepend this to table names. If you need to build an SQL statement and then return it instead of running it, you can set \IPS\Db::i()->returnQuery = TRUE before calling the driver to build a query. To run a MySQL query that has been fully built already represented as a string, you can call the query() method. \IPS\Db::i()->query( "UPDATE some_table SET field_a='value' WHERE id_field=1" ); You should typically avoid using the query() method directly, as the other built in methods automatically handle things like escaping values, prepending the database table prefix and so on. If you need to build a UNION statement, there is a method available to facilitate this. /** * Build UNION statement * * @param array $selects Array of \IPS\Db\Select objects * @param string|NULL $order ORDER BY clause * @param array|int $limit Rows to fetch or array( offset, limit ) * @param string|null $group Group by clause * @param bool $unionAll TRUE to perform a UNION ALL, FALSE (default) to perform a regular UNION * @param int $flags Bitwise flags * @param array|string|NULL $where WHERE clause (see example) * @param string $querySelect Custom select for the outer query * @li \IPS\Db::SELECT_SQL_CALC_FOUND_ROWS Will add SQL_CALC_FOUND_ROWS * @return \IPS\Db|Select */ public function union( $selects, $order, $limit, $group=NULL, $unionAll=FALSE, $flags=0, $where=NULL, $querySelect='*' ) To build a FIND_IN_SET() clause, which allows the query to search for specific values in a MySQL field that contains comma-separated values, you can use the findInSet() method. /** * FIND_IN_SET * Generates a WHERE clause to determine if any value from a column containing a comma-delimined list matches any value from an array * * @param string $column Column name (which contains a comma-delimited list) * @param array $values Acceptable values * @param bool $reverse If true, will match cases where NO values from $column match any from $values * @return string Where clause * @see \IPS\Db::in() More efficient equivilant for columns that do not contain comma-delimited lists */ public function findInSet( $column, $values, $reverse=FALSE ) Similarly, you can build an IN() clause by using the in() method. /** * IN * Generates a WHERE clause to determine if the value of a column matches any value from an array * * @param string $column Column name * @param array $values Acceptable values * @param bool $reverse If true, will match cases where $column does NOT match $values * @return string Where clause * @see \IPS\Db::findInSet() For columns that contain comma-delimited lists */ public function in( $column, $values, $reverse=FALSE ) If you are performing a query against a bitwise column and need to check a value, you can use the bitwiseWhere method (or simply build the WHERE clause manually).. /** * Bitwise WHERE clause * * @param array $definition Bitwise keys as defined by the class * @param string $key The key to check for * @param bool $value Value to check for * @return string * @throws \InvalidArgumentException */ public function bitwiseWhere( $definition, $key, $value=TRUE ) You will find that most of these miscellaneous methods are not referenced or needed as often as the core insert, update, delete, replace and select methods.
  8. Wiki based Dev Docs

    Sorry, I don't watch the forums closely so unless I'm tagged I often don't see things. I will bring this up at our next development meeting and get opinions.
  9. Invision Community 4.2

    You may have misunderstood. Not ALL javascript is combined into one file - instead, multiple files are combined into files, but that does not mean there is only one file as the end result. For example, in your screenshot you see front_front_widgets.js - this would be all files under applications/core/dev/js/front/controllers/widgets/ (there are 4). CSS is similarly grouped so that instead of 50 files being output during normal execution, there are maybe 5 or 10 (it all depends on what page you view).
  10. Dates and Times

    Date and time handling is an important function of the software, and the \IPS\DateTime class provides several utility methods to assist with handling dates and times reliably. It is important to note that the \IPS\DateTime class extends the core PHP DateTime class, so all of the general PHP methods for working with dates and times are immediately available through this interface as well. Dates and times are represented in the database by unix timestamps. When displaying a date to a user, however, we need to convert the timestamp into a human-readable date and time, localized to the viewer's time zone. You can use the ts() static method for this purpose. $time = \IPS\DateTime::ts( $timestamp ); You can also use the create() static method to create a new datetime instance (which will default to the current date/time). $time = \IPS\DateTime::create(); The primary methods you will then use to display the date and/or time are as follows /* Show the date and time in the user's timezone */ print (string) $time; The magic __toString() method will automatically take care of time zone conversions and so on. /* Output a <time> HTML tag with the relative time displayed */ print $time->html( TRUE, FALSE, NULL ); The first parameter determines whether the date/time should be capitalized or not (set this to FALSE if the time will be used in the middle of a sentence for instance), while the second parameter determines if the 'short' version of the date/time should be used even when not on mobile (e.g. 1d instead of 1day). The last parameter allows you to override the member or language to use to format the time. /* Show just the date */ print $time->localeDate(); /* Show just the time - first parameter indicates whether or not to return seconds, while the second parameter indicates whether or not to return minutes */ print $time->localeTime( TRUE, TRUE ); /* Return just the month and day, without the year (or time( print $time->dayAndMonth(); /* Return the date with the 4 digit year */ print $time->fullYearLocaleDate(); RELATIVE_FORMAT_NORMAL );print $time->relative( \IPS\DateTime:: /* Format the relative date/time */ For the relative() method, the following constants are recognized: RELATIVE_FORMAT_NORMAL: Yesterday at 2pm RELATIVE_FORMAT_LOWER: yesterday at 2pm (e.g. "Edited yesterday at 2pm") RELATIVE_FORMAT_SHORT: 1dy (for mobile view) If you need to use a completely custom format, you can use the strFormat() method. While the DateTime class in PHP already has a built in format() method, it is not locale-aware and as such the strFormat() is used instead (which accepts any format accepted by strftime in PHP). print $time->strFormat( '%B' ); Finally, there are some standardized formats supported out of the box as well, primarily useful when specifications require dates to be formatted in a certain way (e.g. RSS) print $time->rfc3339(); // 2017-06-06T11:00:00Z print $time->rfc1123(); // Tuesday, 6 June 2017 11:00:00 GMT It is important to remember when caching data that dates and times should be localized based on the current viewing user's time zone (which is automatically detected). For this reason, you should not cache formatted dates or times, but rather format on display. Finally, there is a "datetime" template plugin which can be used to format dates and times in templates automatically. {datetime="$timestamp"} This will output the localized date and time from the __toString() call. The extra attributes supported by this plugin are: dateonly: Only return the date norelative: Do not return a relative date lowercase: Return the date in lowercase short: Return the short form of the date
  11. Invision Community 4.2

    CSS and Javascript content are already minified and combined (where logical) behind the scenes - no secondary processing needed (this is done by the theme system automatically).
  12. nexus/Item

    What it is The nexus/Item extension allows your application to integrate with Commerce, supporting paid content within a third party application. Downloads uses this integration to better support paid files through Commerce, for instance. How to use The class template includes 4 properties, and various methods depending upon the class you are extending. By default the template is generated as so: class _{class} extends \IPS\nexus\Invoice\Item\Charge // or \IPS\nexus\Invoice\Item\Purchase You will need to decide if you are extending an Item\Charge or an Item\Purchase and adjust accordingly. Once you have done so, you can extend any methods in this class that you wish. The four class properties to define are /** * @brief Application */ public static $application = '{app}'; /** * @brief Application */ public static $type = ''; /** * @brief Icon */ public static $icon = ''; /** * @brief Title */ public static $title = ''; You will specify your application under $application, the type of content under $type, an fa-* icon for $icon, and a language string for $title. From there, you will need to determine which methods you will need to override. Downloads, for instance, overrides the url() and acpUrl() methods to link to the file that was purchased, the image() and purchaseImage() methods to return the screenshot of the file as the image to represent the purchase, the renewalPaymentMethodIds() method to restrict which payment gateways can be used for renewals, and the acpPage() and clientAreaPage() methods to return information about the file when viewing the purchase. You may only need to override onPaid() in order to perform an action when a purchase within your application is marked as paid, or you may need to override other methods as Downloads does.
  13. core/ContentModeratorPermissions

    What it is ContentModeratorPermissions extensions allow your application to add new content item permission options to the moderator configuration area. This extension is very similar to the ModeratorPermissions extension, except that it is specifically designed to allow you to adjust content item permissions. How to use This type of extension will need to be created manually by creating the folder and the class within the folder in your application, rather than through the developer center. Once created, you will be able to define the same 3 methods as you would in a ModeratorPermissions extension: getPermissions(), onChange() and onDelete(). The forum application defines such an extension which allows it to rebuild the search index if moderator permissions for forums where only authors can see their own posts are adjusted. This prevents moderators from being able to see topics posted by other users if they should not be able to.
  14. core/Uninstall

    What it is Uninstall extensions are executed when an application is uninstalled, allowing an application an opportunity to perform any necessary cleanup not automatically performed by Invision Community. Examples include deleting files that may have been stored special during installation, or pinging a remote API to disable the site. How to use The extension defines 3 methods (and a deprecated method left in place for backwards compatibility) /** * Code to execute before the application has been uninstalled * * @param string $application Application directory * @return array */ public function preUninstall( $application ) { } The preUninstall() method is called just prior to the application being uninstalled. This can be useful if you need to look data up before it is deleted, in order to perform the cleanup. /** * Code to execute after the application has been uninstalled * * @param string $application Application directory * @return array */ public function postUninstall( $application ) { } The postUninstall() method is called just after the application has been uninstalled. /** * Code to execute when other applications or plugins are uninstalled * * @param string $application Application directory * @param int $plugin Plugin ID * @return void */ public function onOtherUninstall( $application=NULL, $plugin=NULL ) { } If another application is uninstalled, you may need to take action within your application. For instance, your application may enable certain features integrating with the Calendar application, however if the Calendar application is uninstalled you may need to disable those features. This method allows your application an opportunity to take action when a different application (or plugin) is uninstalled.
  15. core/StreamItems

    What it is The StreamItems extension allows an application to add "extra items" to stream results. While content stored in the search index (which includes content item data) is automatically returned in activity streams, if you want to show other data such as users signing up in an application or users performing another action in an application in activity streams, you will need to use a StreamItems extension to accomplish this. Calendar uses this functionality to show when a user has RSVP'd for an event in activity streams, for example. How to use The extension will define a single method where you will fetch and return extra stream results /** * Is there content to display? * * @param \IPS\Member|NULL $author The author to limit extra items to * @param Timestamp|NULL $lastTime If provided, only items since this date are included. If NULL, it works out which to include based on what results are being shown * @param Timestamp|NULL $firstTime If provided, only items before this date are included. If NULL, it works out which to include based on what results are being shown * @return array Array of \IPS\Content\Search\Result\Custom objects */ public function extraItems( $author=NULL, $lastTime=NULL, $firstTime=NULL ) { // Note! // Your application must define a setting and a language string in the format of "all_activity_{application}_{extensionname}" all in lower case. Without this, this plugin will not be executed. // This setting will automatically be used to store the administrators choice of whether to show this data or not. } return array(); You need to define a specific language string as shown in the code comment above for the extension to process, however otherwise this is all you need. If the content is being limited by author, the $author parameter will be set to an \IPS\Member object, otherwise it will be set to NULL. Similarly, $lastTime and $firstTime may be set to timestamps or may be set to NULL ($lastTime will typically be set, however). You will then fetch any results that match the parameters supplied, and create an array of \IPS\Content\Search\Result\Custom objects to return. /** * Is there content to display? * * @param \IPS\Member|NULL $author The author to limit extra items to * @param Timestamp|NULL $lastTime If provided, only items since this date are included. If NULL, it works out which to include based on what results are being shown * @param Timestamp|NULL $firstTime If provided, only items before this date are included. If NULL, it works out which to include based on what results are being shown * @return array Array of \IPS\Content\Search\Result\Custom objects */ public function extraItems( $author=NULL, $lastTime=NULL, $firstTime=NULL ) { $results = array(); $where = array( array( 'date>?', $lastTime ) ); if ( $firstTime ) { $where[] = array( 'date<?', $firstTime ); } if ( $author ) { $where[] = array( 'author_id=?', $author->member_id ); } foreach ( \IPS\Db::i()->select( '*', 'my_table', $where, 'date DESC', 10 ) as $row ) { $results[] = new \IPS\Content\Search\Result\Custom( \IPS\DateTime::ts( $row[ 'date' ] ), \IPS\Member::loggedIn()->language()->addToStack( 'some_language_string' ) ); } return $results; }