Version 4.2.0

This commit is contained in:
Neo committed 2025-12-19 05:44:59 -08:00
1 parent 0dc2aee5ed
commit 96997ddd8e
2074 files changed
+125454 -55780

No files matched your search

@@ -1,13 +1,12 @@
<?php
/**
* @brief Calendar Node
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Community Suite
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Calendar
* @since 18 Dec 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\calendar;
@@ -24,6 +23,8 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _Calendar extends \IPS\Node\Model implements \IPS\Node\Permissions
{
use \IPS\Content\ClubContainer;
/**
* @brief [ActiveRecord] Multiton Store
*/
@@ -113,6 +114,18 @@ class _Calendar extends \IPS\Node\Model implements \IPS\Node\Permissions
* @brief [Node] Prefix string that is automatically prepended to permission matrix language strings
*/
public static $permissionLangPrefix = 'calperm_';
/**
* @brief Bitwise values for cal_bitoptions field
*/
public static $bitOptions = array(
'calendar_bitoptions' => array(
'calendar_bitoptions' => array(
'bw_disable_tagging' => 1,
'bw_disable_prefixes' => 2
)
)
);
/**
* [Node] Set whether or not this node is enabled
@@ -155,6 +168,13 @@ class _Calendar extends \IPS\Node\Model implements \IPS\Node\Permissions
$form->add( new \IPS\Helpers\Form\YesNo( 'cal_comment_moderate', $this->id ? $this->comment_moderate : FALSE, FALSE, array(), NULL, NULL, NULL, 'cal_comment_moderate' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'cal_allow_reviews', $this->id ? $this->allow_reviews : FALSE, FALSE, array( 'togglesOn' => array( 'cal_review_moderate' ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'cal_review_moderate', $this->id ? $this->review_moderate : FALSE, FALSE, array(), NULL, NULL, NULL, 'cal_review_moderate' ) );
if ( \IPS\Settings::i()->tags_enabled )
{
$form->addHeader( 'tags' );
$form->add( new \IPS\Helpers\Form\YesNo( 'bw_disable_tagging', !$this->calendar_bitoptions['bw_disable_tagging'], FALSE, array( 'togglesOn' => array( 'bw_disable_prefixes' ) ), NULL, NULL, NULL, 'bw_disable_tagging' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'bw_disable_prefixes', !$this->calendar_bitoptions['bw_disable_prefixes'], FALSE, array(), NULL, NULL, NULL, 'bw_disable_prefixes' ) );
}
}
/**
@@ -178,6 +198,16 @@ class _Calendar extends \IPS\Node\Model implements \IPS\Node\Permissions
unset( $values['cal_title'] );
}
/* Bitwise */
foreach ( array( 'bw_disable_tagging', 'bw_disable_prefixes' ) as $k )
{
if( isset( $values[ $k ] ) )
{
$values['calendar_bitoptions'][ $k ] = !$values[ $k ];
unset( $values[ $k ] );
}
}
return $values;
}
@@ -269,4 +299,43 @@ class _Calendar extends \IPS\Node\Model implements \IPS\Node\Permissions
\IPS\Output::i()->headCss = \IPS\Output::i()->headCss . $output;
}
/* !Clubs */
/**
* Get front-end language string
*
* @return string
*/
public static function clubFrontTitle()
{
return 'calendars_sg';
}
/**
* Set form for creating a node of this type in a club
*
* @param \IPS\Helpers\Form $form Form object
* @return void
*/
public function clubForm( \IPS\Helpers\Form $form )
{
$itemClass = static::$contentItemClass;
$form->add( new \IPS\Helpers\Form\Text( 'club_node_name', $this->_id ? $this->_title : \IPS\Member::loggedIn()->language()->addToStack( $itemClass::$title . '_pl' ), TRUE, array( 'maxLength' => 255 ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'cal_allow_comments', $this->id ? $this->allow_comments : TRUE, FALSE, array( 'togglesOn' => array( 'cal_comment_moderate' ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'cal_allow_reviews', $this->id ? $this->allow_reviews : FALSE, FALSE, array( 'togglesOn' => array( 'cal_review_moderate' ) ) ) );
}
/**
* Class-specific routine when saving club form
*
* @param \IPS\Member\Club $club The club
* @param array $values Values
* @return void
*/
public function _saveClubForm( \IPS\Member\Club $club, $values )
{
$this->allow_comments = $values['cal_allow_comments'];
$this->allow_reviews = $values['cal_allow_reviews'];
}
}
+18 -9
View File
@@ -1,13 +1,12 @@
<?php
/**
* @brief Calendar-specific date functions
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Community Suite
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Calendar
* @since 30 Dec 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\calendar;
@@ -428,18 +427,28 @@ class _Date extends \IPS\DateTime
* @return string
*/
public function calendarDate( $memberOrLanguage=NULL )
{
return static::determineLanguage( $memberOrLanguage )->convertString( strftime( static::calendarDateFormat(), $this->getTimestamp() + $this->offset ) );
}
/**
* Return the format used by calendarDate()
*
* @return string
*/
public static function calendarDateFormat()
{
if( \IPS\Settings::i()->calendar_date_format == -1 AND \IPS\Settings::i()->calendar_date_format_custom )
{
return static::determineLanguage( $memberOrLanguage )->convertString( strftime( \IPS\Settings::i()->calendar_date_format_custom, $this->getTimestamp() + $this->offset ) );
return \IPS\Settings::i()->calendar_date_format_custom;
}
elseif( isset( static::$dateFormats[ \IPS\Settings::i()->calendar_date_format ] ) )
{
return static::determineLanguage( $memberOrLanguage )->convertString( strftime( static::$dateFormats[ \IPS\Settings::i()->calendar_date_format ], $this->getTimestamp() + $this->offset ) );
return static::$dateFormats[ \IPS\Settings::i()->calendar_date_format ];
}
else
{
return static::localeDate();
return '%x';
}
}
@@ -778,6 +787,6 @@ class _Date extends \IPS\DateTime
*/
public function localeTime( $seconds=TRUE, $minutes=TRUE, $memberOrLanguage=NULL )
{
return static::determineLanguage( $memberOrLanguage )->convertString( strftime( $this->localeTimeFormat( $seconds, $minutes, $memberOrLanguage ), $this->getTimestamp() + $this->offset ) );
return static::determineLanguage( $memberOrLanguage )->convertString( strftime( static::localeTimeFormat( $seconds, $minutes, $memberOrLanguage ), $this->getTimestamp() + $this->offset ) );
}
}
+29 -11
View File
@@ -1,13 +1,12 @@
<?php
/**
* @brief Event Comment Model
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Community Suite
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Calendar
* @since 7 Jan 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\calendar\Event;
@@ -22,8 +21,10 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
/**
* Event Comment Model
*/
class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \IPS\Content\ReportCenter, \IPS\Content\Hideable, \IPS\Content\Reputation, \IPS\Content\Searchable, \IPS\Content\Embeddable
class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \IPS\Content\ReportCenter, \IPS\Content\Hideable, \IPS\Content\Searchable, \IPS\Content\Embeddable
{
use \IPS\Content\Reactable;
/**
* @brief [ActiveRecord] Multiton Store
*/
@@ -75,11 +76,6 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
*/
public static $icon = 'calendar';
/**
* @brief Reputation Type
*/
public static $reputationType = 'comment_id';
/**
* @brief [Content] Key for hide reasons
*/
@@ -138,4 +134,26 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
return \IPS\Theme::i()->getTemplate( 'global', 'calendar', 'front' )->searchResultCommentSnippet( $indexData, $nextOccurance, $startDate, $endDate, $itemData['event_all_day'], $reviewRating, $view == 'condensed' );
}
/**
* Reaction Type
*
* @return string
*/
public static function reactionType()
{
return 'comment_id';
}
/**
* Get content for embed
*
* @param array $params Additional parameters to add to URL
* @return string
*/
public function embedContent( $params )
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'embed.css', 'calendar', 'front' ) );
return \IPS\Theme::i()->getTemplate( 'global', 'calendar' )->embedEventComment( $this, $this->item(), $this->url()->setQueryString( $params ) );
}
}
+233 -36
View File
@@ -1,13 +1,12 @@
<?php
/**
* @brief Calendar Event Model
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Community Suite
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Calendar
* @since 7 Jan 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\calendar;
@@ -25,15 +24,17 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
class _Event extends \IPS\Content\Item implements
\IPS\Content\Permissions,
\IPS\Content\Tags,
\IPS\Content\Reputation,
\IPS\Content\Followable,
\IPS\Content\ReportCenter,
\IPS\Content\ReadMarkers,
\IPS\Content\Hideable, \IPS\Content\Featurable, \IPS\Content\Lockable,
\IPS\Content\Shareable,
\IPS\Content\Searchable,
\IPS\Content\Embeddable
\IPS\Content\Embeddable,
\IPS\Content\MetaData
{
use \IPS\Content\Reactable;
/**
* @brief Application
*/
@@ -101,6 +102,7 @@ class _Event extends \IPS\Content\Item implements
'ip_address' => 'ip_address',
'cover_photo' => 'cover_photo',
'cover_photo_offset' => 'cover_offset',
'meta_data' => 'meta_data'
);
/**
@@ -118,11 +120,6 @@ class _Event extends \IPS\Content\Item implements
*/
public static $formLangPrefix = 'event_';
/**
* @brief Reputation Type
*/
public static $reputationType = 'event_id';
/**
* @brief Cover Photo Storage Extension
*/
@@ -132,6 +129,11 @@ class _Event extends \IPS\Content\Item implements
* @brief Cached date objects
*/
protected $dateObjects = array( 'start' => NULL, 'end' => NULL );
/**
* @brief Cached venue
*/
protected $venueObject = NULL;
/**
* @brief [Content] Key for hide reasons
@@ -923,23 +925,39 @@ class _Event extends \IPS\Content\Item implements
/**
* Are comments supported by this class?
*
* @param \IPS\Member\NULL $member The member to check for or NULL fto not check permission
* @return int
* @param \IPS\Member|NULL $member The member to check for or NULL to not check permission
* @param \IPS\Node\Model|NULL $container The container to check in, or NULL for any container
* @return bool
*/
public static function supportsComments( \IPS\Member $member = NULL )
{
return parent::supportsComments() and ( !$member or \IPS\calendar\Calendar::countWhere( 'read', $member, array( 'cal_allow_comments=1' ) ) );
public static function supportsComments( \IPS\Member $member = NULL, \IPS\Node\Model $container = NULL )
{
if( $container !== NULL )
{
return parent::supportsComments() and $container->allow_comments AND ( !$member or $container->can( 'read', $member ) );
}
else
{
return parent::supportsComments() and ( !$member or \IPS\calendar\Calendar::countWhere( 'read', $member, array( 'cal_allow_comments=1' ) ) );
}
}
/**
* Are reviews supported by this class?
*
* @param \IPS\Member\NULL $member The member to check for or NULL to not check permission
* @return int
* @param \IPS\Member|NULL $member The member to check for or NULL to not check permission
* @param \IPS\Node\Model|NULL $container The container to check in, or NULL for any container
* @return bool
*/
public static function supportsReviews( \IPS\Member $member = NULL )
public static function supportsReviews( \IPS\Member $member = NULL, \IPS\Node\Model $container = NULL )
{
return parent::supportsReviews() and ( !$member or \IPS\calendar\Calendar::countWhere( 'read', $member, array( 'cal_allow_reviews=1' ) ) );
if( $container !== NULL )
{
return parent::supportsReviews() and $container->allow_reviews AND ( !$member or $container->can( 'read', $member ) );
}
else
{
return parent::supportsReviews() and ( !$member or \IPS\calendar\Calendar::countWhere( 'read', $member, array( 'cal_allow_reviews=1' ) ) );
}
}
/**
@@ -952,11 +970,11 @@ class _Event extends \IPS\Content\Item implements
$tabs = array();
if ( $this->container()->allow_reviews )
{
$tabs['reviews'] = \IPS\Member::loggedIn()->language()->pluralize( \IPS\Member::loggedIn()->language()->get( 'event_review_count' ), array( $this->mapped('num_reviews') ) );
$tabs['reviews'] = \IPS\Member::loggedIn()->language()->addToStack( 'event_review_count', TRUE, array( 'pluralize' => array( $this->mapped('num_reviews') ) ) );
}
if ( $this->container()->allow_comments )
{
$tabs['comments'] = \IPS\Member::loggedIn()->language()->pluralize( \IPS\Member::loggedIn()->language()->get( 'event_comment_count' ), array( $this->mapped('num_comments') ) );
$tabs['comments'] = \IPS\Member::loggedIn()->language()->addToStack( 'event_comment_count', TRUE, array( 'pluralize' => array( $this->mapped('num_comments') ) ) );
}
return $tabs;
@@ -1174,13 +1192,16 @@ class _Event extends \IPS\Content\Item implements
}, NULL, NULL, 'event_dates' );
/* Init */
$return['calendar'] = new \IPS\Helpers\Form\Node( static::$formLangPrefix . 'container', $container ? $container->id : ( isset( \IPS\Request::i()->calendar ) ? \IPS\Request::i()->calendar : NULL ), TRUE, array(
'url' => \IPS\Http\Url::internal( 'app=calendar&module=calendar&controller=submit', 'front', 'calendar_submit' ),
'class' => 'IPS\calendar\Calendar',
'permissionCheck' => 'add',
'togglePerm' => 'askrsvp',
'toggleIds' => array( 'event_rsvp' ),
) );
if ( !$container or !$container->club() )
{
$return['calendar'] = new \IPS\Helpers\Form\Node( static::$formLangPrefix . 'container', $container ? $container->id : ( isset( \IPS\Request::i()->calendar ) ? \IPS\Request::i()->calendar : NULL ), TRUE, array(
'url' => \IPS\Http\Url::internal( 'app=calendar&module=calendar&controller=submit', 'front', 'calendar_submit' ),
'class' => 'IPS\calendar\Calendar',
'permissionCheck' => 'add',
'togglePerm' => 'askrsvp',
'toggleIds' => array( 'event_rsvp' ),
) );
}
/* Get default elements */
$return = array_merge( $return, parent::formElements( $item, $container ) );
@@ -1190,7 +1211,27 @@ class _Event extends \IPS\Content\Item implements
/* Cover photo and location */
$return['header'] = new \IPS\Helpers\Form\Upload( 'event_cover_photo', ( $item AND $item->cover_photo ) ? \IPS\File::get( 'calendar_Events', $item->cover_photo ) : NULL, FALSE, array( 'image' => TRUE, 'storageExtension' => 'calendar_Events' ) );
$return['location'] = new \IPS\Helpers\Form\Address( 'event_location', ( $item AND $item->location ) ? \IPS\GeoLocation::buildFromJson( $item->location ) : NULL, FALSE, array( 'minimize' => TRUE, 'requireFullAddress' => FALSE ) );
if( \IPS\Settings::i()->calendar_venues_enabled )
{
$return['venue'] = new \IPS\Helpers\Form\Node( static::$formLangPrefix . 'venue', ( $item ) ? $item->venue ?: 0 : ( isset( \IPS\Request::i()->venue ) ? \IPS\Request::i()->venue : NULL ), FALSE, array(
'url' => \IPS\Http\Url::internal( 'app=calendar&module=calendar&controller=submit', 'front', 'calendar_submit' ),
'class' => 'IPS\calendar\Venue',
'zeroVal' => 'venues_not_listed',
'zeroValTogglesOn' => array( 'event_location', 'event_new_venue' ),
'permissionCheck' => 'add',
) );
}
$return['location'] = new \IPS\Helpers\Form\Address( 'event_location', ( $item AND $item->location ) ? \IPS\GeoLocation::buildFromJson( $item->location ) : NULL, FALSE, array( 'minimize' => TRUE, 'requireFullAddress' => FALSE ), NULL, NULL, NULL, 'event_location' );
/* Save location as a new venue? */
if ( \IPS\Settings::i()->calendar_venues_enabled and \IPS\Member::loggedIn()->hasAcpRestriction( 'calendar', 'calendars', 'venues_manage' ) )
{
$return['save_new_venue'] = new \IPS\Helpers\Form\YesNo( 'event_new_venue', NULL, FALSE, array( 'togglesOn' => array( 'venue_title', 'venue_description' ) ), NULL, NULL, NULL, 'event_new_venue' );
$return['venue_title'] = new \IPS\Helpers\Form\Translatable( 'venue_title', NULL, TRUE, array( 'app' => 'calendar', 'key' => NULL ), NULL, NULL, NULL, 'venue_title' );
$return['venue_description'] = new \IPS\Helpers\Form\Translatable( 'venue_description', NULL, FALSE, array( 'app' => 'calendar', 'key' => NULL, 'editor' => array( 'app' => 'calendar', 'key' => 'Venue', 'autoSaveKey' => "calendar-new-venue", 'attachIds' => NULL ) ), NULL, NULL, NULL, 'venue_description' );
}
/* Gallery album association */
if( \IPS\Application::appIsEnabled( 'gallery' ) )
@@ -1246,8 +1287,11 @@ class _Event extends \IPS\Content\Item implements
}
/* Calendar */
$this->calendar_id = ( $values[ static::$formLangPrefix . 'container' ] instanceof \IPS\Node\Model ) ? $values[ static::$formLangPrefix . 'container' ]->_id : intval( $values[ static::$formLangPrefix . 'container' ] );
if ( isset( $values[ static::$formLangPrefix . 'container' ] ) )
{
$this->calendar_id = ( $values[ static::$formLangPrefix . 'container' ] instanceof \IPS\Node\Model ) ? $values[ static::$formLangPrefix . 'container' ]->_id : intval( $values[ static::$formLangPrefix . 'container' ] );
}
/* Start and end dates */
$this->start_date = \IPS\calendar\Date::createFromForm( $values['event_dates']['start_date'], ( ( !isset( $values['event_dates']['all_day'] ) OR !$values['event_dates']['all_day'] ) ? $values['event_dates']['start_time'] : NULL ), ( isset( $values['event_dates']['all_day'] ) AND $values['event_dates']['all_day'] ) ? 'UTC' : $values['event_dates']['event_timezone'] )->format( 'Y-m-d H:i' );
$this->end_date = null;
@@ -1308,6 +1352,37 @@ class _Event extends \IPS\Content\Item implements
/* Set location */
$this->location = ( $values['event_location'] !== NULL ) ? json_encode( $values['event_location'] ) : NULL;
if( \IPS\Settings::i()->calendar_venues_enabled )
{
$this->venue = NULL;
if( $values['event_venue'] instanceof \IPS\calendar\Venue )
{
$this->venue = $values['event_venue']->_id;
}
elseif ( \IPS\Member::loggedIn()->hasAcpRestriction( 'calendar', 'calendars', 'venues_manage' ) and $values['event_new_venue'] and $values['event_location'] )
{
$venue = new \IPS\calendar\Venue;
$venue->address = json_encode( $values['event_location'] );
$venue->save();
\IPS\Lang::saveCustom( 'calendar', 'calendar_venue_' . $venue->id, $values['venue_title'] );
\IPS\Lang::saveCustom( 'calendar', 'calendar_venue_' . $venue->id . '_desc', $values['venue_description'] );
$venue->title_seo = \IPS\Http\Url\Friendly::seoTitle( $values['venue_title'][ \IPS\Lang::defaultLanguage() ] );
/* Set the order */
$order = \IPS\Db::i()->select( array( "MAX( `venue_position` )" ), 'calendar_venues', array() )->first();
$venue->position = $order + 1;
$venue->save();
/* Set the new event to this venue */
$this->venue = $venue->id;
$this->location = NULL;
}
}
/* Gallery album association */
if( \IPS\Application::appIsEnabled( 'gallery' ) AND $values['event_album'] instanceof \IPS\gallery\Album )
{
@@ -1322,6 +1397,10 @@ class _Event extends \IPS\Content\Item implements
$this->rsvp = isset( $values['event_rsvp'] ) ? $values['event_rsvp'] : FALSE;
$this->rsvp_limit = isset( $values['event_rsvp_limit'] ) ? $values['event_rsvp_limit'] : 0;
unset( $values['save_new_venue'] );
unset( $values['venue_title'] );
unset( $values['venue_description'] );
/* Get the normal stuff */
parent::processForm( $values );
}
@@ -1337,6 +1416,8 @@ class _Event extends \IPS\Content\Item implements
{
parent::processAfterCreate( $comment, $values );
$this->expireWidgetCaches();
/* And claim attachments */
\IPS\File::claimAttachments( 'calendar-event', $this->id );
@@ -1344,6 +1425,19 @@ class _Event extends \IPS\Content\Item implements
$this->expireWidgetCaches();
}
/**
* Process created object AFTER the object has been edited
*
* @param array $values Values from form
* @return void
*/
public function processAfterEdit( $values )
{
parent::processAfterEdit( $values );
$this->expireWidgetCaches();
}
/**
* Create Notification
*
@@ -1366,7 +1460,8 @@ class _Event extends \IPS\Content\Item implements
parent::delete();
\IPS\Db::i()->delete( 'calendar_event_rsvp', array( 'rsvp_event_id=?', $this->id ) );
\IPS\Db::i()->delete( 'calendar_event_reminders', array( 'reminder_event_id=?', $this->id ) );
/* We should not delete maps for imported events, because we do not want to reimport them */
//\IPS\Db::i()->delete( 'calendar_import_map', array( 'import_event_id=?', $this->id ) );
}
@@ -1381,6 +1476,20 @@ class _Event extends \IPS\Content\Item implements
*/
public function map( $width, $height )
{
/* Show venue map */
if( \IPS\Settings::i()->calendar_venues_enabled )
{
if( $this->venue )
{
try
{
$this->location = \IPS\calendar\Venue::load( $this->venue )->address;
}
catch( \OutOfRangeException $e ){}
}
}
/* No venue? Do we have a specific event location? */
if( $this->location )
{
try
@@ -1402,11 +1511,12 @@ class _Event extends \IPS\Content\Item implements
* @param int|null $limit Maximum number of events to return (only supported when not formatting events)
* @param bool $formatEvents Whether or not to format events into a structured array
* @param \IPS\Member $member The member (NULL to use currently logged in member)
* @param \IPS\calendar\Venue $venue The venue to filter by (if provided)
* @return \IPS\Patterns\ActiveRecordIterator
* @throws \InvalidArgumentException
* @see \IPS\Content\_Item::getItemsWithPermission()
*/
public static function retrieveEvents( $startDate, $endDate=NULL, $calendar=NULL, $limit=NULL, $formatEvents=TRUE, $member=NULL )
public static function retrieveEvents( $startDate, $endDate=NULL, $calendar=NULL, $limit=NULL, $formatEvents=TRUE, $member=NULL, $venue=NULL )
{
$where = array();
@@ -1423,6 +1533,11 @@ class _Event extends \IPS\Content\Item implements
}
}
if( $venue!== NULL )
{
$where[] = array( 'event_venue=?', (int) $venue->_id );
}
if( $endDate === NULL AND $formatEvents === TRUE )
{
throw new \InvalidArgumentException;
@@ -1700,7 +1815,8 @@ class _Event extends \IPS\Content\Item implements
*/
public function embedContent( $params )
{
return \IPS\Theme::i()->getTemplate( 'global', 'calendar' )->embedCalendar_event( $this, $this->url()->setQueryString( $params ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'embed.css', 'calendar', 'front' ) );
return \IPS\Theme::i()->getTemplate( 'global', 'calendar' )->embedEvent( $this, $this->url()->setQueryString( $params ), $this->embedImage() );
}
/**
@@ -1812,4 +1928,85 @@ class _Event extends \IPS\Content\Item implements
$member = $member ?: \IPS\Member::loggedIn();
return parent::canReview( $member ) and $this->container()->allow_reviews;
}
/**
* Reaction Type
*
* @return string
*/
public static function reactionType()
{
return 'event_id';
}
/**
* Can remind?
*
* @return bool
*/
public function canRemind()
{
/* Does the event happen more than 1 day into the future? */
return (bool) ( $this->_start_date->sub( new \DateInterval( 'P1D' ) ) > \IPS\DateTime::create() );
}
/* !Tags */
/**
* Can tag?
*
* @param \IPS\Member|NULL $member The member to check for (NULL for currently logged in member)
* @param \IPS\Node\Model|NULL $container The container to check if tags can be used in, if applicable
* @return bool
*/
public static function canTag( \IPS\Member $member = NULL, \IPS\Node\Model $container = NULL )
{
return parent::canTag( $member, $container ) and ( $container === NULL or !$container->calendar_bitoptions['bw_disable_tagging'] );
}
/**
* Can use prefixes?
*
* @param \IPS\Member|NULL $member The member to check for (NULL for currently logged in member)
* @param \IPS\Node\Model|NULL $container The container to check if tags can be used in, if applicable
* @return bool
*/
public static function canPrefix( \IPS\Member $member = NULL, \IPS\Node\Model $container = NULL )
{
return parent::canPrefix( $member, $container ) and ( $container === NULL or !$container->calendar_bitoptions['bw_disable_prefixes'] );
}
/**
* Venue
*
* @return NULL|\IPS\calendar\Venue
*/
public function venue()
{
if ( \IPS\Settings::i()->calendar_venues_enabled and $this->venue )
{
try
{
if ( !$this->venueObject )
{
$this->venueObject = \IPS\calendar\Venue::load( $this->venue );
}
return $this->venueObject;
}
catch( \OutOfRangeException $e ){}
}
return NULL;
}
/**
* Supported Meta Data Types
*
* @return array
*/
public static function supportedMetaDataTypes()
{
return array( 'core_FeaturedComments', 'core_ContentMessages' );
}
}
+29 -11
View File
@@ -1,13 +1,12 @@
<?php
/**
* @brief Event Review Model
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Community Suite
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Calendar
* @since 7 Jan 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\calendar\Event;
@@ -22,8 +21,10 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
/**
* Event Review Model
*/
class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \IPS\Content\ReportCenter, \IPS\Content\Hideable, \IPS\Content\Reputation, \IPS\Content\Searchable, \IPS\Content\Embeddable
class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \IPS\Content\ReportCenter, \IPS\Content\Hideable, \IPS\Content\Searchable, \IPS\Content\Embeddable
{
use \IPS\Content\Reactable;
/**
* @brief [ActiveRecord] Multiton Store
*/
@@ -79,11 +80,6 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \
*/
public static $icon = 'calendar';
/**
* @brief Reputation Type
*/
public static $reputationType = 'review_id';
/**
* @brief [Content] Key for hide reasons
*/
@@ -126,4 +122,26 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \
{
return \IPS\calendar\Event\Comment::searchResultSnippet( $indexData, $authorData, $itemData, $containerData, $reputationData, $reviewRating, $view );
}
/**
* Reaction Type
*
* @return string
*/
public static function reactionType()
{
return 'review_id';
}
/**
* Get content for embed
*
* @param array $params Additional parameters to add to URL
* @return string
*/
public function embedContent( $params )
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'embed.css', 'calendar', 'front' ) );
return \IPS\Theme::i()->getTemplate( 'global', 'calendar' )->embedEventReview( $this, $this->item(), $this->url()->setQueryString( $params ) );
}
}
@@ -1,13 +1,12 @@
<?php
/**
* @brief iCalendar ICS Parser
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Community Suite
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Calendar
* @since 19 Dec 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\calendar\Icalendar;
@@ -246,10 +245,11 @@ class _ICSParser
* @param int|\IPS\calendar\Calendar $calendar The calendar to import to
* @param int|\IPS\Member $member The member the imported events should be 'from'
* @param int $feed The feed we are importing (used to detect and prevent duplicate imports)
* @param int $venue The venue if provided
* @return array Number of events imported and skipped
* @throws \UnexpectedValueException
*/
public function parse( $content, $calendar, $member, $feed=NULL )
public function parse( $content, $calendar, $member, $feed=NULL, $venue=NULL )
{
/* Load the calendar */
$this->calendar = ( $calendar instanceof \IPS\calendar\Calendar ) ? $calendar : \IPS\calendar\Calendar::load( $calendar );
@@ -416,6 +416,12 @@ class _ICSParser
$newEvent->post_key = md5( uniqid() );
$newEvent->member_id = $this->member->member_id;
$newEvent->calendar_id = $this->calendar->id;
if( $venue )
{
$newEvent->venue = $venue;
}
$newEvent->approved = 1;
/* RSVP ? */
@@ -447,6 +453,19 @@ class _ICSParser
}
catch( \BadFunctionCallException $e ){}
}
elseif( isset( $event['location'] ) )
{
try
{
$location = new \IPS\GeoLocation;
$location->addressLines = array( $event['location'] );
$location->getLatLong();
/* We get by LatLong because getLatLong() does not populate the other address data. */
$newEvent->location = json_encode( \IPS\GeoLocation::getByLatLong( $location->lat, $location->long ) );
}
catch( \BadFunctionCallException $e ){}
}
/* Save */
$newEvent->save();
@@ -866,7 +885,6 @@ class _ICSParser
*
* @param string $text String to unencode
* @return string Unencoded string
* @link http://community.invisionpower.com/tracker/issue-33787-formatting-problem-with-imported-ics-file-calendar-app/
*/
protected function _unencodeSpecialCharacters( $text )
{
@@ -930,42 +948,50 @@ class _ICSParser
*/
protected function _unparseTimeInfo( $string )
{
if( mb_strpos( $string, '=' ) !== false )
/* init */
$value = NULL;
$rawTime = NULL;
$tzid = NULL;
/* split the timestamp from the properties */
$dt = explode( ':', $string );
/* Set the raw time value */
$rawTime = $dt[1];
/* Which properties do we have */
$properties = explode( ';', $dt[0] );
foreach( $properties as $property )
{
$_tmp = explode( '=', $string );
$_key = explode( ';', $_tmp[0] );
$tmp = explode( ':', $_tmp[1] );
}
else
{
$_tmp = array();
$_key = array();
$tmp = explode( ':', $string );
/* The date string may be in the format of "DTSTART;20160219T140000Z" (for example) */
if ( !isset( $tmp[1] ) )
if( mb_strpos( $property, '=' ) !== false )
{
if ( mb_strpos( $string, ';' ) !== false )
$_property = explode( '=', $property );
/* Grab our needed values from the given properties */
switch( $_property[0] )
{
$tmp = explode( ';', $string );
case 'VALUE':
$value = $_property[1];
break;
case 'TZID':
$tzid = $_property[1];
break;
}
}
}
/* Got a TZID? */
$tzid = ( count( $_key ) AND $_key[1] == 'TZID' ) ? $tmp[0] : '';
/* If the value is a date without a time, then type should be reset to date */
$type = ( mb_strlen( $tmp[1] ) === 8 ) ? 'DATE' : 'DATETIME';
$type = ( mb_strlen( $rawTime ) === 8 ) ? 'DATE' : 'DATETIME';
/* Format return array */
$return = array(
'type' => $type,
'raw' => $tmp[1],
'raw_ts' => strtotime( $tmp[1] ),
'tzid' => str_replace( '"', '', $tzid ),
'raw' => $rawTime,
'raw_ts' => strtotime( $rawTime ),
'tzid' => ( $tzid ) ? str_replace( '"', '', $tzid ) : '',
);
/* Is this the earliest or latest timestamp? */
if ( ( $this->_feedEarliest == 0 ) OR ( $return['raw_ts'] < $this->_feedEarliest ) )
{
@@ -1025,7 +1051,6 @@ class _ICSParser
*
* @param int $start Line number
* @return void
* @link http://community.invisionpower.com/resources/bugs.html/_/ip-calendar/recurring-events-can-sometimes-be-skipped-in-ics-r41033
*/
protected function _processEventObject( $start )
{
@@ -1070,7 +1095,6 @@ class _ICSParser
break;
case 'SUMMARY':
/* @link http://community.invisionpower.com/tracker/issue-32941-ical-summary/ */
if( mb_strpos( $_data, 'LANGUAGE=' ) === 0 )
{
$_data = preg_replace( "/^LANGUAGE=(.+?):(.+?)$/i", "\\2", $_data );
@@ -1290,7 +1314,7 @@ class _ICSParser
{
$event[ $method ]['utc_time'] = new \IPS\DateTime( $event[ $method ]['raw'], new \DateTimeZone( $event[ $method ]['tzid'] ) );
}
else
elseif( isset( $event[ $method ]['raw'] ) )
{
$event[ $method ]['utc_time'] = new \IPS\DateTime( $event[ $method ]['raw'] );
}
@@ -1,13 +1,12 @@
<?php
/**
* @brief iCalendar active record
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Community Suite
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Calendar
* @since 19 Dec 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\calendar;
@@ -65,7 +64,7 @@ class _Icalendar extends \IPS\Patterns\ActiveRecord
$icsParser = new \IPS\calendar\Icalendar\ICSParser;
return $icsParser->parse( \IPS\Http\Url::external( str_replace( array( 'webcal://', 'webcals://' ), array( 'http://', 'https://' ), $this->url ) )->request()->get(), $this->calendar_id, $this->member_id, $this->id );
return $icsParser->parse( \IPS\Http\Url::external( str_replace( array( 'webcal://', 'webcals://' ), array( 'http://', 'https://' ), $this->url ) )->request()->get(), $this->calendar_id, $this->member_id, $this->id, $this->venue_id );
}
/**
@@ -0,0 +1,219 @@
<?php
/**
* @brief Venue Node
*/
namespace IPS\calendar;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Calendar Node
*/
class _Venue extends \IPS\Node\Model
{
/**
* @brief [ActiveRecord] Multiton Store
*/
protected static $multitons;
/**
* @brief [ActiveRecord] Database Table
*/
public static $databaseTable = 'calendar_venues';
/**
* @brief [ActiveRecord] Database Prefix
*/
public static $databasePrefix = 'venue_';
/**
* @brief [Node] Order Database Column
*/
public static $databaseColumnOrder = 'position';
/**
* @brief [Node] Node Title
*/
public static $nodeTitle = 'venues';
/**
* @brief URL Base
*/
public static $urlTemplate = 'calendar_venue';
/**
* @brief URL Base
*/
public static $urlBase = 'app=calendar&module=calendar&controller=venue&id=';
/**
* @brief [Node] ACP Restrictions
* @code
array(
'app' => 'core', // The application key which holds the restrictrions
'module' => 'foo', // The module key which holds the restrictions
'map' => array( // [Optional] The key for each restriction - can alternatively use "prefix"
'add' => 'foo_add',
'edit' => 'foo_edit',
'permissions' => 'foo_perms',
'delete' => 'foo_delete'
),
'all' => 'foo_manage', // [Optional] The key to use for any restriction not provided in the map (only needed if not providing all 4)
'prefix' => 'foo_', // [Optional] Rather than specifying each key in the map, you can specify a prefix, and it will automatically look for restrictions with the key "[prefix]_add/edit/permissions/delete"
* @endcode
*/
protected static $restrictions = array(
'app' => 'calendar',
'module' => 'venues',
'prefix' => 'venues_'
);
/**
* @brief [Node] Title prefix. If specified, will look for a language key with "{$key}_title" as the key
*/
public static $titleLangPrefix = 'calendar_venue_';
/**
* @brief [Node] Description suffix. If specified, will look for a language key with "{$titleLangPrefix}_{$id}_{$descriptionLangSuffix}" as the key
*/
public static $descriptionLangSuffix = '_desc';
/**
* @brief [Node] Moderator Permission
*/
public static $modPerm = 'calendar_venues';
/**
* [Node] Get whether or not this node is enabled
*
* @note Return value NULL indicates the node cannot be enabled/disabled
* @return bool|null
*/
protected function get__enabled()
{
return (bool) $this->enabled;
}
/**
* [Node] Set whether or not this node is enabled
*
* @param bool|int $enabled Whether to set it enabled or disabled
* @return void
*/
protected function set__enabled( $enabled )
{
$this->enabled = $enabled;
}
/**
* @brief Cached URL
*/
protected $_url = NULL;
/**
* Get SEO name
*
* @return string
*/
public function get_title_seo()
{
if( !$this->_data['title_seo'] )
{
$this->title_seo = \IPS\Http\Url\Friendly::seoTitle( \IPS\Lang::load( \IPS\Lang::defaultLanguage() )->get( 'calendar_venue_' . $this->id ) );
$this->save();
}
return $this->_data['title_seo'] ?: \IPS\Http\Url\Friendly::seoTitle( \IPS\Lang::load( \IPS\Lang::defaultLanguage() )->get( 'calendar_venue_' . $this->id ) );
}
/**
* [Node] Add/Edit Form
*
* @param \IPS\Helpers\Form $form The form
* @return void
*/
public function form( &$form )
{
$form->add( new \IPS\Helpers\Form\Translatable( 'venue_title', NULL, TRUE, array( 'app' => 'calendar', 'key' => ( $this->id ? "calendar_venue_{$this->id}" : NULL ) ) ) );
$form->add( new \IPS\Helpers\Form\Translatable( 'venue_description', NULL, FALSE, array( 'app' => 'calendar', 'key' => ( $this->id ? "calendar_venue_{$this->id}_desc" : NULL ), 'editor' => array( 'app' => 'calendar', 'key' => 'Venue', 'autoSaveKey' => ( $this->id ? "calendar-venue-{$this->id}" : "calendar-new-venue" ), 'attachIds' => $this->id ? array( $this->id, NULL, 'description' ) : NULL ) ) ) );
$form->add( new \IPS\Helpers\Form\Address( 'venue_address', $this->id ? \IPS\GeoLocation::buildFromJson( $this->address ) : NULL, TRUE, array( 'minimize' => FALSE, 'requireFullAddress' => TRUE ), NULL, NULL, NULL, 'venue_address' ) );
}
/**
* [Node] Format form values from add/edit form for save
*
* @param array $values Values from the form
* @return array
*/
public function formatFormValues( $values )
{
if ( !$this->id )
{
$this->save();
\IPS\File::claimAttachments( 'calendar-new-venue', $this->id, NULL, 'description', TRUE );
}
if( isset( $values['venue_title'] ) )
{
\IPS\Lang::saveCustom( 'calendar', 'calendar_venue_' . $this->id, $values['venue_title'] );
$values['title_seo'] = \IPS\Http\Url\Friendly::seoTitle( $values['venue_title'][ \IPS\Lang::defaultLanguage() ] );
unset( $values['venue_title'] );
}
if( isset( $values['venue_description'] ) )
{
\IPS\Lang::saveCustom( 'venues', 'calendar_venue_' . $this->id .'_desc', $values['venue_description'] );
unset( $values['venue_description'] );
}
$values['venue_address'] = ( $values['venue_address'] !== NULL ) ? json_encode( $values['venue_address'] ) : NULL;
return $values;
}
/**
* @brief SEO Title Column
*/
public static $seoTitleColumn = 'title_seo';
/**
* [ActiveRecord] Delete Record
*
* @return void
*/
public function delete()
{
\IPS\Lang::deleteCustom( 'calendar', 'calendar_venue_' . $this->id . '_desc' );
return parent::delete();
}
/**
* Return the map for the venue
*
* @param int $width Width
* @param int $height Height
* @return string
* @note \BadMethodCallException can be thrown if the google maps integration is shut off - don't show any error if that happens.
*/
public function map( $width, $height )
{
if( $this->address )
{
try
{
return \IPS\GeoLocation::buildFromJson( $this->address )->map()->render( $width, $height );
}
catch( \BadMethodCallException $e ){}
}
return '';
}
}