CMS  Version 3.9
merge_code_manager.inc
Go to the documentation of this file.
1 <?php
7 /**************************************************************
8 
9 Copyright (c) 2007,2008 Sonjara, Inc
10 
11 Permission is hereby granted, free of charge, to any person
12 obtaining a copy of this software and associated documentation
13 files (the "Software"), to deal in the Software without
14 restriction, including without limitation the rights to use,
15 copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the
17 Software is furnished to do so, subject to the following
18 conditions:
19 
20 The above copyright notice and this permission notice shall be
21 included in all copies or substantial portions of the Software.
22 
23 Except as contained in this notice, the name(s) of the above
24 copyright holders shall not be used in advertising or otherwise
25 to promote the sale, use or other dealings in this Software
26 without prior written authorization.
27 
28 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
30 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
32 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
33 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
34 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
35 OTHER DEALINGS IN THE SOFTWARE.
36 
37 *****************************************************************/
38 
39 /*
40  * Title: email_manager.inc
41  *
42  * Description: Class for constructing email messages from
43  * a template and sending emails.
44  *
45  * author: Janice Gallant for Sonjara, Inc.
46  *
47  * date: 5/13/10
48  *
49  ***************************************************************/
50 
51 /*
52  *
53  * Allow class_name to be passed b/c some email templates
54  * have a different class_name for merge codes than the item
55  * for special handling.
56  *
57  */
59 {
60  var $item;
61  var $patterns;
64 
65  function MergeCodeManager($item, $inText, $class_name = "")
66  {
67  $this->item = clone($item);
68  $this->item->filter = null;
69  $this->class_name = ($class_name) ? $class_name : get_class($item);
70 
71  $usedMergeCodes = array();
72  preg_match_all("/\[(.*?)\]/", $inText, $usedMergeCodes, PREG_PATTERN_ORDER);
73 
74  $usedMergeCodes = $usedMergeCodes[1];
75  $this->getMergeCodeValues($usedMergeCodes);
76 
77  }
78 
79 
80  /*
81  * Get all the merge codes used in the recipient, subject, and
82  * message fields in the template.
83  *
84  * Given the merge codes in use in the template, retrieve the
85  * maps for those merge codes in the database.
86  *
87  * For each used merge code, if a record exists in merge
88  * code table, then use that map; if not, treat the
89  * code itself as a map. This allows fields in the
90  * sending class to be accessed without a merge code record.
91  * E.g., merge code "first_name" access the first_name field
92  * in the sending class.
93  *
94  * Even if replace text is empty, still add to
95  * list to get rid of code from the text (e.g., [merge_code]).
96  * Search on the "dirty" name that could contain stray
97  * html so that the text is replaced.
98  */
99  function getMergeCodeValues($usedMergeCodes)
100  {
101  if(count($usedMergeCodes) > 0)
102  {
103  $used = strip_tags("'". implode("','", array_values($usedMergeCodes)) . "'");
104 
105  $mergeCodes = Query::create(MergeCode, "WHERE class_name = :cn and name in ($used)")
106  ->bind(":cn", $this->class_name)
107  ->execute();
108  }
109 
110  if(count($mergeCodes) > 0)
111  $indexed = reindexList($mergeCodes, "name");
112  else
113  $indexed = array();
114 
115  if(count($usedMergeCodes) == 0)
116  {
117  trace("MergeCodeManager:: no merge codes used.", 3);
118  return;
119  }
120 
121  $patterns = array();
122  $replacements = array();
123 
124  foreach($usedMergeCodes as $idx => $name)
125  {
126  $clean_name = strip_tags($name);
127  if(array_key_exists($clean_name, $indexed))
128  $mergeCode = $indexed[$clean_name];
129  else
130  {
131  $mergeCode = new MergeCode();
132  $mergeCode->map = $clean_name;
133  }
134 
135  $map = ($mergeCode) ? $mergeCode->map : $clean_name;
136 
137  trace("MergeCodeManager:: map is $map", 3);
138 
139  if($map)
140  $replaceText = $this->getReplaceText($map);
141 
142  array_push($patterns, "[$name]");
143  array_push($replacements, $replaceText);
144  }
145 
146  $this->patterns = $patterns;
147  $this->replacements = $replacements;
148  }
149 
150 
151  /*
152  * If the mergeCode map has 2 parts, then possible interpretations:
153  * a) Part 1 is relation and part 2 is field of relation
154  * b) part 1 is relation and part 2 is function in that relation
155  * c) part 1 is a class and part2 is a static function in that class
156  *
157  *
158  * If the mergeCode map has 1 part, then it must be
159  * a) callback function, no class
160  * b) function of calling class
161  * c) field of calling class
162  *
163  * Check if has field before check if is_callable in case the field
164  * name could be interpreted as callable (e.g., "date)
165  */
166  function getReplaceText($mergeCodeMap)
167  {
168  $valid = true;
169 
170  list($part1, $part2) = explode(".", $mergeCodeMap);
171 
172  trace("MergeCodeManager::part1 is $part1 and part2 is $part2", 3);
173 
174  if(get_class($this->item) == $part1)
175  {
176  trace("MergeCodeManager::part1 equals item class", 3);
177  $part1 = $part2;
178  $part2 = "";
179  }
180 
181  if($part2)
182  {
183  if($this->item->hasRelation($part1))
184  {
185  $replaceObj = $this->item->$part1();
186  if(is_object($replaceObj) && $replaceObj->hasField($part2))
187  {
188  trace("MergeCodeManager:: part1 is relation and part2 is a field in that related class.", 3);
189  $replaceText = $replaceObj->format("{".$part2 ."}");
190  }
191  elseif(is_callable(array($replaceObj, $part2)))
192  {
193  trace("MergeCodeManager:: part1 is relation of sending class and part2 is a function in that relation.", 3);
194  $replaceText = $replaceObj->$part2();
195  }
196  else
197  {
198  $valid = false;
199  }
200  }
201  elseif(is_callable(array($part1, $part2)))
202  {
203  trace("MergeCodeManager:: part1 is class and part2 is static function of a class.", 3);
204  $replaceText = call_user_func(array($part1, $part2), $this->item);
205  }
206  else
207  {
208  $valid = false;
209  }
210  }
211  elseif($part1) // Map has just one part
212  {
213  if($this->item->hasField($part1))
214  {
215  trace("MergeCodeManager:: part1 is field of sending class.", 3);
216  $replaceText = $this->item->format("{".$part1 ."}");
217  }
218  elseif(is_callable($part1))
219  {
220  trace("MergeCodeManager:: part1 is callback function.", 3);
221  $replaceText = call_user_func($part1, $this->item);
222  }
223  elseif(is_callable(array($this->item, $part1)))
224  {
225  trace("MergeCodeManager:: part1 is function from sending class.", 3);
226  $replaceText = $this->item->$part1();
227  }
228  else
229  {
230  $valid = false;
231  }
232  }
233 
234  if($valid)
235  trace("MergeCodeManager:: replaceText is $replaceText", 3);
236  else
237  trace("MergeCodeManager:: Warning: merge code map not valid.", 3);
238 
239  return $replaceText;
240  }
241 
243  {
244  $text = $this->item->format($text);
245 
246  if(count($this->patterns) == 0)
247  return $text;
248  else
249  return str_replace($this->patterns, $this->replacements, $text);
250  }
251 
253  {
254  $classes = array();
255  $classes = ComponentManager::fireEvent("EnumerateDataItemClasses", $classes);
256 
257  $mergeCodes = Query::create(MergeCode, "WHERE class_name=:cn")
258  ->bind(":cn", $class_name)
259  ->execute();
260 
261  if(array_search($class_name, $classes))
262  {
263  $obj = new $class_name();
265  }
266 
267  $table = new DataListView($mergeCodes, "mergeCodes");
268  $table->column("Code Name", "{name}", false)
269  ->column("Description", "{description}", false)
270  ;
271  $table->emptyMessage = "There are no merge codes defined.";
272  $table->sortable = false;
273  $table->cssStyle = "width: 100%";
274 
275  return $table;
276  }
277 
278 
279 /*
280  * Any of the sending class's fields can be used as a merge
281  * code w/o creating a merge_code record. Since primary or
282  * foreign key fields are not useful as codes, omit these from
283  * the list. Also omit composite_class fields and passwords.
284  */
285  static function getDefaultCodes($obj, $mergeCodes)
286  {
287  $fields = $obj->getFields();
288  if(count($fields) > 0)
289  {
290  foreach($fields as $name => $type)
291  {
292  if(preg_match("/_id/", $name) || preg_match("/composite/", $name)
293  || preg_match("/password/", $name) || $type == Boolean)
294  continue;
295 
296  $mCode = new MergeCode();
297  $mCode->name = $name;
298  $mCode->map = "N/A";
299  $mCode->description = $type ." field in ". get_class($obj);
300  $mergeCodes[] = $mCode;
301  }
302  }
303  return $mergeCodes;
304  }
305 }
306 ?>
$name
Definition: upload.inc:54
static fireEvent($event, $parameter=null, $mustBeConsumed=false)
Fire an event to all subscribers as detailed in their manifests.
MergeCodeManager($item, $inText, $class_name="")
static buildMergeCodeTable($class_name)
getReplaceText($mergeCodeMap)
static getDefaultCodes($obj, $mergeCodes)
getMergeCodeValues($usedMergeCodes)
$mergeCode
$mergeCodes