1: <?php
2: /*****************************************************************************************
3: * X2Engine Open Source Edition is a customer relationship management program developed by
4: * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
5: *
6: * This program is free software; you can redistribute it and/or modify it under
7: * the terms of the GNU Affero General Public License version 3 as published by the
8: * Free Software Foundation with the addition of the following permission added
9: * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10: * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
11: * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
12: *
13: * This program is distributed in the hope that it will be useful, but WITHOUT
14: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15: * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
16: * details.
17: *
18: * You should have received a copy of the GNU Affero General Public License along with
19: * this program; if not, see http://www.gnu.org/licenses or write to the Free
20: * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21: * 02110-1301 USA.
22: *
23: * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
24: * California 95067, USA. or at email address [email protected].
25: *
26: * The interactive user interfaces in modified source and object code versions
27: * of this program must display Appropriate Legal Notices, as required under
28: * Section 5 of the GNU Affero General Public License version 3.
29: *
30: * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31: * these Appropriate Legal Notices must retain the display of the "Powered by
32: * X2Engine" logo. If the display of the logo is not reasonably feasible for
33: * technical reasons, the Appropriate Legal Notices must display the words
34: * "Powered by X2Engine".
35: *****************************************************************************************/
36:
37: /**
38: * This is the model class for table "x2_tags".
39: *
40: * @package application.models
41: * @property integer $id
42: * @property string $type
43: * @property integer $itemId
44: * @property string $taggedBy
45: * @property string $tag
46: * @property integer $timestamp
47: * @property string $itemName
48: */
49: class Tags extends CActiveRecord {
50:
51: const DELIM = ',';
52:
53: /**
54: * Returns the static model of the specified AR class.
55: * @return Tags the static model class
56: */
57: public static function model($className=__CLASS__) {
58: return parent::model($className);
59: }
60:
61: /**
62: * @return string the associated database table name
63: */
64: public function tableName() {
65: return 'x2_tags';
66: }
67:
68: /**
69: * @return array validation rules for model attributes.
70: */
71: public function rules() {
72: // NOTE: you should only define rules for those attributes that
73: // will receive user inputs.
74: return array(
75: array ('tag', 'validateTag'),
76: array('type, itemId, taggedBy, tag', 'required'),
77: array('itemId, timestamp', 'numerical', 'integerOnly'=>true),
78: array('type, taggedBy', 'length', 'max'=>50),
79: array('tag, itemName', 'length', 'max'=>250),
80: array(
81: 'tag',
82: 'application.extensions.unique-attributes-validator.UniqueAttributesValidator',
83: 'with'=>'tag,type,itemId',
84: 'binary'=>true,
85: ),
86: // The following rule is used by search().
87: // Please remove those attributes that should not be searched.
88: array('id, type, itemId, taggedBy, tag, timestamp, itemName', 'safe', 'on'=>'search'),
89: );
90: }
91:
92: /**
93: * Normalizes tag format before all other forms of tag validation
94: */
95: public function validateTag ($attr) {
96: $this->$attr = self::normalizeTag ($this->$attr);
97: }
98:
99: /**
100: * @return array customized attribute labels (name=>label)
101: */
102: public function attributeLabels() {
103: return array(
104: 'id' => 'ID',
105: 'type' => 'Type',
106: 'itemId' => 'Item',
107: 'taggedBy' => 'Tagged By',
108: 'tag' => 'Tag',
109: 'timestamp' => 'Timestamp',
110: 'itemName' => 'Item Name',
111: );
112: }
113:
114: /*
115: * Returns a list of all existing tags, without the # at the beginning
116: */
117: public static function getAllTags() {
118: $tags = Yii::app()->db->createCommand()
119: ->selectDistinct('tag')
120: ->from('x2_tags')
121: ->order('tag DESC')
122: ->queryColumn();
123:
124: foreach ($tags as &$tag) {
125: $tag = substr($tag, 1);
126: }
127:
128: return $tags;
129: }
130:
131: /**
132: * Return a list of tag links associated with a specified model
133: * @param $model Model type, e.g., "Contacts"
134: * @param $id Model ID
135: * @param $limit Number of tags to return, or -1 to disable
136: * @return string HTML containing links to each tag
137: */
138: public static function getTagLinks($model,$id,$limit = -1) {
139: // Disable limit in CDbCriteria with a value less than 0
140: if(!is_numeric($limit) || empty($limit))
141: $limit = -1;
142:
143: $tags = Tags::model()->findAllByAttributes(
144: array('type'=>$model,'itemId'=>$id),
145: new CDbCriteria(array('order'=>'id DESC','limit'=>$limit))
146: );
147: $tagCount = Tags::model()->countByAttributes(array('type'=>$model,'itemId'=>$id));
148:
149: $links = array();
150: foreach($tags as &$tag) {
151: $links[] = CHtml::link(
152: CHtml::encode($tag->tag),array('/search/search','term'=>CHtml::encode($tag->tag)));
153: }
154: if($limit !== -1 && $tagCount > $limit)
155: $links[] = '...';
156:
157: return implode(' ',$links);
158: }
159:
160:
161:
162: /**
163: * Retrieves a list of models based on the current search/filter conditions.
164: * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.
165: */
166: public function search() {
167: // Warning: Please modify the following code to remove attributes that
168: // should not be searched.
169:
170: $criteria=new CDbCriteria;
171:
172: $criteria->compare('id',$this->id);
173: $criteria->compare('type',$this->type,true);
174: $criteria->compare('itemId',$this->itemId);
175: $criteria->compare('taggedBy',$this->taggedBy,true);
176: $criteria->compare('tag',$this->tag,true);
177: $criteria->compare('timestamp',$this->timestamp);
178: $criteria->compare('itemName',$this->itemName,true);
179:
180: return new CActiveDataProvider(get_class($this), array(
181: 'criteria'=>$criteria,
182: ));
183: }
184:
185: /**
186: * Splits the provided string on commas, removes spaces, makes sure each tag has a hash
187: * @param string $str a string containing 1 or more comma-separated tags
188: * @param boolean $suppressHash if true, hash tag will not be prepended to tag and any existing
189: * leading hash tag will be removed.
190: * @return array the properly formatted tags
191: */
192: public static function parseTags($str, $suppressHash=false) {
193: $tags = array();
194:
195: foreach(explode(self::DELIM,$str) as $tag) { // split the string
196: $tag = trim($tag);
197: if(strlen($tag) > 0) { // eliminate empty tags
198: $tags[] = self::normalizeTag ($tag, $suppressHash);
199: }
200: }
201: return $tags;
202: }
203:
204: public static function normalizeTag ($tag, $suppressHash=false) {
205: $tag = trim($tag);
206: if (strpos ($tag, self::DELIM) !== false) {
207: $tag = strtr($tag,array(self::DELIM => ''));
208: }
209: if(substr($tag,0,1) !== '#' && !$suppressHash) // make sure they have the hash
210: $tag = '#'.$tag;
211: if (substr ($tag, 0, 1) === '#' && $suppressHash) {
212: $tag = preg_replace ('/^#/', '', $tag);
213: }
214: return $tag;
215: }
216:
217: public static function normalizeTags (array $tags, $suppressHash=false) {
218: foreach ($tags as &$tag) {
219: $tag = Tags::normalizeTag ($tag, $suppressHash);
220: }
221: return $tags;
222: }
223:
224: }
225: