晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂树,芳草鲜美,落英缤纷。渔人甚异之,复前行,欲穷其林。 林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田、美池、桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。 见渔人,乃大惊,问所从来。具答之。便要还家,设酒杀鸡作食。村中闻有此人,咸来问讯。自云先世避秦时乱,率妻子邑人来此绝境,不复出焉,遂与外人间隔。问今是何世,乃不知有汉,无论魏晋。此人一一为具言所闻,皆叹惋。余人各复延至其家,皆出酒食。停数日,辞去。此中人语云:“不足为外人道也。”(间隔 一作:隔绝) 既出,得其船,便扶向路,处处志之。及郡下,诣太守,说如此。太守即遣人随其往,寻向所志,遂迷,不复得路。 南阳刘子骥,高尚士也,闻之,欣然规往。未果,寻病终。后遂无问津者。
|
Server : Apache System : Linux srv.rainic.com 4.18.0-553.47.1.el8_10.x86_64 #1 SMP Wed Apr 2 05:45:37 EDT 2025 x86_64 User : rainic ( 1014) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/akaindir/public_html/crm/modules/Mobile/third-party/qCal/qCal/ |
Upload File : |
<?php
/**
* Base calendar component class. Events, Todos, and Calendars are
* examples of components in qCal
* @package qCal
* @copyright Luke Visinoni (luke.visinoni@gmail.com)
* @author Luke Visinoni (luke.visinoni@gmail.com)
* @license GNU Lesser General Public License
*
* RFC 2445 Definition
*
* The body of the iCalendar object consists of a sequence of calendar
* properties and one or more calendar components. The calendar
* properties are attributes that apply to the calendar as a whole. The
* calendar components are collections of properties that express a
* particular calendar semantic. For example, the calendar component can
* specify an event, a to-do, a journal entry, time zone information, or
* free/busy time information, or an alarm.
*
* The body of the iCalendar object is defined by the following
* notation:
*
* icalbody = calprops component
*
* calprops = 2*(
*
* ; 'prodid' and 'version' are both REQUIRED,
* ; but MUST NOT occur more than once
*
* prodid /version /
*
* ; 'calscale' and 'method' are optional,
* ; but MUST NOT occur more than once
*
* calscale /
* method /
*
* x-prop
*
* )
*
* component = 1*(eventc / todoc / journalc / freebusyc /
* / timezonec / iana-comp / x-comp)
*
* iana-comp = "BEGIN" ":" iana-token CRLF
*
* 1*contentline
*
* "END" ":" iana-token CRLF
*
* x-comp = "BEGIN" ":" x-name CRLF
*
* 1*contentline
*
* "END" ":" x-name CRLF
*
* An iCalendar object MUST include the "PRODID" and "VERSION" calendar
* properties. In addition, it MUST include at least one calendar
* component. Special forms of iCalendar objects are possible to publish
* just busy time (i.e., only a "VFREEBUSY" calendar component) or time
* zone (i.e., only a "VTIMEZONE" calendar component) information. In
* addition, a complex iCalendar object is possible that is used to
* capture a complete snapshot of the contents of a calendar (e.g.,
* composite of many different calendar components). More commonly, an
* iCalendar object will consist of just a single "VEVENT", "VTODO" or
* "VJOURNAL" calendar component.
*/
abstract class qCal_Component {
/**
* The name of this component
* @var string
*/
protected $name;
/**
* Contains a list of allowed parent components.
* @var array
*/
protected $allowedComponents = array();
/**
* Contains an array of this component's child components (if any). It uses
* @var array
*/
protected $children = array();
/**
* Contains an array of this component's properties. Properties provide
* information about their respective components. This array is associative.
* It uses property name as key and property object as value (or array of them
* if said property can be set multiple times). This is so that I can quickly
* look up any certain property.
* @var array
*/
protected $properties = array();
/**
* Contains an array of this component's required properties
*/
protected $requiredProperties = array();
/**
* Parent component (all components but vcalendar should have one once attached)
*/
protected $parent;
/**
* Class constructor
* Accepts an array of properties, which can be simple values or actual property objects
* Pass in a null value to use a property's default value (some dont have defaults, so beware)
* Example:
* $cal = new qCal_Component_Calendar(array(
* 'prodid' => '-// Some Property Id//',
* 'someotherproperty' => null,
* qCal_Property_Version(2.0),
* ), array(
* qCal_Component_Daylight(),
* ));
*/
public function __construct($properties = array(), $components = array()) {
foreach ($components as $component) {
// if value is an array, then each value inside of it will be a component
if ($component instanceof qCal_Component) {
$this->attach($component);
}
else throw new qCal_Exception_InvalidComponent('The second argument is optional, but if provided, must be an array of components');
}
foreach ($properties as $name => $value) {
// if value is an array, then each value inside of it will be a property
if (is_array($value)) {
foreach ($value as $val) {
if ($val instanceof qCal_Property) {
$this->addProperty($val);
} else {
$this->addProperty($name, $val);
}
}
} else {
if ($value instanceof qCal_Property) {
$this->addProperty($value);
} else {
$this->addProperty($name, $value);
}
}
}
// I think it would make more sense to do validation at render time. That way you don't have
// to have all of the required components and properties when you instantiate. Also, that way
// components don't need to be aware of eachother until render time (or until validate() is called
// explicitly). @todo
// $this->validate();
}
/**
* @todo (lazy load functionality) Check that this is a valid component. This method is sort of lazy-loaded. It only gets called
* if the user has requested data that requires validation and the component has not been validated already.
* @todo Shouldn't this loop over children and validate them too? Maybe optionally?
*/
public function validate() {
// if we're missing any required properties and they have no default, throw an exception
$properties = array();
foreach ($this->getProperties() as $property) {
if (is_array($property)) {
foreach ($property as $prop) {
$properties[] = $prop->getName();
}
} else {
$properties[] = $property->getName();
}
}
$missing = array_diff($this->requiredProperties, array_unique($properties));
foreach ($missing as $propertyname) {
// the property factory will throw an exception if it's passed a null value for a property with no default
try {
$property = qCal_Property::factory($propertyname, null);
$this->addProperty($property);
} catch (qCal_Exception_InvalidPropertyValue $e) {
// if that's the case, catch the exception and throw a missing property exception
throw new qCal_Exception_MissingProperty($this->getName() . " component requires " . $propertyname . " property");
}
}
// this allows per-component validation :)
$this->doValidation();
}
/**
* Returns the component name
* @return string
*/
public function getName() {
return $this->name;
}
/**
* Returns true if this component can be attached to $component
* I'm sure there's a better way to do this, but this works for now
*/
public function canAttachTo(qCal_Component $component) {
if (in_array($component->getName(), $this->allowedComponents)) return true;
}
/**
* Attach a component to this component (alarm inside event for example)
* @todo There may be an issue with the way this is done. When parsing a file, if a component
* or property with a tzid comes before its corresponding vtimezone component, an exception
* will be thrown. I'm don't think the RFC specifies that requirement (that timezone components
* must come before their corresponding tzids)
* @todo Sub-components such as Vevent need to be able to access the main vcalendar object
* for several reasons.
* - If a vtodo has a tzid, it needs to be able to determine that the corresponding
* vtimezone component is available.
* - If components need to relate to eachother, they can only find eachother through
* the main vcalendar object.
* - Freebusy time can only be determined by polling all components in the main vcalendar
* object.
* - More to come probably
*/
public function attach(qCal_Component $component) {
if (!$component->canAttachTo($this)) {
throw new qCal_Exception_InvalidComponent($component->getName() . ' cannot be attached to ' . $this->getName());
}
$component->setParent($this);
// make sure if a timezone is requested that it is available...
$timezones = $this->getTimezones();
$tzids = array_keys($timezones);
// we only need to check if tzid exists if we are attaching something other than a timezone...
if (!($component instanceof qCal_Component_Vtimezone)) {
foreach ($component->getProperties() as $pname => $properties) {
$pname = strtoupper($pname); // probably redundant...
foreach ($properties as $property) {
switch ($pname) {
case "TZID":
$tzid = $property->getValue();
if (!array_key_exists($tzid, $tzids)) {
throw new qCal_Exception_MissingComponent('TZID "' . $tzid . '" not defined');
}
break;
}
$params = $property->getParams();
foreach ($params as $param => $val) {
$param = strtoupper($param); // probably redundant...
switch ($param) {
case "TZID":
$tzid = $val;
if (!array_key_exists($tzid, $tzids)) {
throw new qCal_Exception_MissingComponent('TZID "' . $tzid . '" not defined');
}
break;
}
}
}
}
}
$this->children[$component->getName()][] = $component;
}
/**
* Set the parent of this component
* @todo I'm not sure this will suffice. See the attach method for reasoning behind this.
*/
public function setParent(qCal_Component $component) {
$this->parent = $component;
}
/**
* Get the parent of this component (if there is one)
*/
public function getParent() {
return $this->parent;
}
/**
* The only thing I need this for so far is the parser, but it may come in handy for the facade as well
*/
static public function factory($name, $properties = array()) {
if (empty($name)) return false;
// capitalize
$component = ucfirst(strtolower($name));
$className = "qCal_Component_" . $component;
$fileName = str_replace("_", DIRECTORY_SEPARATOR, $className) . ".php";
qCal_Loader::loadFile($fileName);
$class = new $className($properties);
return $class;
}
/**
* I'm not sure how this should work. Not sure if it should be setProperty,
* addProperty, both? Because properties on some components can be set multiple
* times, while some properties have multiple values. :( I am trying to consider
* a case where somebody needs to open a calendar, change a few properties on a
* component (change event time for instance). I think the way I'll handle properties
* that can be set multiple times is I'll create a method do delete properties based
* on values, parameters, etc. since they don't really have IDs. So I tihnk I'll go
* with addProperty :)
*/
public function addProperty($property, $value = null, $params = array()) {
if (!($property instanceof qCal_Property)) {
$property = qCal_Property::factory($property, $value, $params);
}
if (!$property->of($this)) {
throw new qCal_Exception_InvalidProperty($this->getName() . " component does not allow " . $property->getName() . " property");
}
if (!$property->allowMultiple()) {
unset($this->properties[$property->getName()]);
}
$this->properties[$property->getName()][] = $property;
}
/**
* Returns property of this component by name
*
* @todo Since the same property can appear in a component more than once, this method
* doesn't make that much sense unless it returns all of the instances of the property
* @return array of qCal_Property
*/
public function getProperty($name) {
$name = strtoupper($name);
if ($this->hasProperty($name)) {
return $this->properties[$name];
}
}
/**
* Returns true if this component contains a property of $name
*
* @return boolean
*/
public function hasProperty($name) {
$name = strtoupper($name);
return isset($this->properties[$name]);
}
/**
* Returns true if this component contains a property of $name
*
* @return boolean
*/
public function hasComponent($name) {
$name = strtoupper($name);
return isset($this->children[$name]);
}
/**
* Returns the child component requested
*/
public function getComponent($name) {
$name = strtoupper($name);
if ($this->hasComponent($name)) {
return $this->children[$name];
}
}
/*
public function clearProperties() {
$this->properties = array();
}
public function clearChildren() {
$this->children = array();
}
*/
public function getProperties() {
return $this->properties;
}
public function getChildren() {
return $this->children;
}
/**
* Gets the parent-most component in the tree. I would really like to come up
* with a cleaner way to access other components from within a component, but oh well.
*/
public function getRootComponent() {
$parent = $this;
while (!($parent instanceof qCal_Component_Vcalendar)) {
if (!$parent->getParent()) break;
$parent = $parent->getParent();
}
return $parent;
}
/**
* Renders the calendar, by default in icalendar format. If you pass
* in a renderer, it will use that instead
*
* @return mixed Depends on the renderer
* @todo Would it make more sense to pass the component to the renderer, or the renderer
* to the component? I'm not sure components should know about rendering.
*/
public function render(qCal_Renderer $renderer = null) {
$this->validate();
if (is_null($renderer)) $renderer = new qCal_Renderer_iCalendar();
return $renderer->render($this);
}
/**
* Output the icalendar component as a string (render it)
*/
public function __toString() {
return $this->render();
}
/**
* getFreeBusyTime
* Looks through all of the data in the calendar and returns a qCal_Component_Vfreebusy object
* with free/busy time from $startdate to $enddate. The component will contain all components, but some
* may have their transparency set to "transparent".
* @todo This cannot be finished until recurring events are finished, since free/busy does not allow
* recurrence rules, each instance of a recurrence would need to be calculated out and passed into the free/busy
* component, so that the component would contain concrete instances of each event recurrence.
*/
public function getFreeBusyTime() {
$root = $this->getRootComponent();
foreach ($root->getChildren() as $children) {
foreach ($children as $child) {
// now get the object's free/busy time
}
}
}
/**
* getTimeZones
*/
public function getTimezones() {
$tzs = array();
$root = $this->getRootComponent();
foreach ($root->getChildren() as $children) {
foreach ($children as $child) {
// if the child is a vtimezone, add it to the results
// @todo make sure that tzid is available, throw exception otherwise
if ($child instanceof qCal_Component_Vtimezone) {
$tzid = $child->getTzid();
$tzid = strtoupper($tzid);
$tzs[$tzid] = $child;
}
}
}
return $tzs;
}
/**
* Get a specific timezone by tzid
* @param string The timezone identifier
*/
public function getTimezone($tzid) {
$tzid = strtoupper($tzid);
$root = $this->getRootComponent();
$timezones = $root->getTimezones();
if (array_key_exists($tzid, $timezones)) {
return $timezones[$tzid];
}
return false;
}
/**
* Allows for components to get and set property values by calling
* qCal_Component::getPropertyName() and qCal_Component::setPropertyName('2.0') where propertyName is the property name
* to be set and $val is the property value.
* This is just a convenience facade, it isn't going to be used within the library as much as by end-users
* @todo I can't decided whether to maybe get rid of the facade methods at least for now since some properties
* can potentially return multiple values and that makes the interface inconsistent
*/
public function __call($method, $params) {
$firstthree = substr($method, 0, 3);
$name = substr($method, 3);
if ($firstthree == "get") {
// if property is allowed multiple times, an array is returned, otherwise just the one component
if ($this->hasProperty($name)) {
$property = $this->getProperty($name);
if (!$property[0]->allowMultiple()) {
return $property[0];
} else {
return $property;
}
}
} elseif ($firstthree == "set") {
$value = isset($params[0]) ? $params[0] : null;
$params = isset($params[1]) ? $params[1] : array();
$property = qCal_Property::factory($name, $value, $params);
$this->addProperty($property);
} elseif ($firstthree == "add") {
// add property type
$property = qCal_Property::factory($name, $params);
$this->addProperty($property);
return $this;
}
// throw exception here?
// throw new qCal_Exception();
}
}