PageRenderTime 31ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/code/formfield/RecipientImportField.php

https://github.com/lenix/silverstripe-newsletter
PHP | 397 lines | 254 code | 89 blank | 54 comment | 25 complexity | 5141fe0282f0f576cd554c469417b449 MD5 | raw file
  1. <?php
  2. /**
  3. * Displays a field for importing recipients.
  4. *
  5. * @package newsletter
  6. */
  7. class RecipientImportField extends FormField {
  8. protected $memberGroup;
  9. protected $memberClass;
  10. protected $tableColumns;
  11. protected $table;
  12. protected $clientFileName;
  13. protected $typeID;
  14. static $column_types = array(
  15. 'Salutation' => array( 'title', 'salutation' ),
  16. 'FirstName' => array( 'firstname', 'christianname', 'givenname' ),
  17. 'Surname' => array( 'lastname','surname', 'familyname' ),
  18. 'Email' => array( 'email', 'emailaddress' ),
  19. 'Address' => array( 'address' ),
  20. 'PhoneNumber' => array('phone','phonenumber'),
  21. 'JobTitle' => array( 'jobtitle' ),
  22. 'Organisation' => array( 'organisation', 'organization' ),
  23. 'EmailType' => array( 'htmlorplaintext', 'emailtype' )
  24. );
  25. static $custom_set_fields = array();
  26. public static function setCustomField( $getMapCode, $fieldName ) {
  27. self::$custom_set_fields[] = array(
  28. 'Code' => $getMapCode,
  29. 'Field' => $fieldName
  30. );
  31. }
  32. function __construct( $name, $title, $memberGroup, $memberClass = 'Member', $form = null ) {
  33. $this->memberGroup = $memberGroup;
  34. $this->memberClass = $memberClass;
  35. parent::__construct( $name, $title, null, $form );
  36. }
  37. function Field() {
  38. $frameURL = Director::absoluteBaseURL() . 'admin/newsletter/displayfilefield/' . $this->typeID;
  39. return "<iframe name=\"{$this->Name}\" frameborder=\"0\" class=\"RecipientImportField\" src=\"$frameURL\"></iframe>";
  40. }
  41. function setTypeID( $id ) {
  42. $this->typeID = $id;
  43. }
  44. function CustomSetFields() {
  45. $fields = new FieldSet();
  46. foreach( self::$custom_set_fields as $customField ) {
  47. eval( '$map = ' . $customField['Code'] .';' );
  48. $noneMap = array( 0 => '(Not set)' );
  49. $map = $noneMap + $map;
  50. $fields->push( new DropdownField( 'Set['.$customField['Field'].']', $customField['Field'], $map ) );
  51. }
  52. return $fields;
  53. }
  54. /**
  55. * Returns HTML to be displayed inside the IFrame
  56. */
  57. static function fileupload() {
  58. }
  59. /**
  60. * Returns the table of results to be displayed in the table of
  61. * details loaded from the file
  62. */
  63. function displaytable() {
  64. // Check that a file was uploaded
  65. $tempFile = fopen( $_FILES['ImportFile']['tmp_name'], 'r' );
  66. // display some error if the file cannot be opened
  67. if(!$tempFile) {
  68. return 'The selected file did not arrive at the server';
  69. }
  70. $this->clientFileName = $_FILES['ImportFile']['name'];
  71. $table = $sessionTable = array();
  72. while( ( $row = fgetcsv( $tempFile ) ) !== false ) {
  73. if( !$this->tableColumns ) {
  74. $this->parseTableHeader( $row );
  75. } else {
  76. $newRow = array();
  77. $newSessionTableRow = array();
  78. foreach( $row as $cell ) {
  79. $newRow[] = $this->parseCSVCell( $cell );
  80. $newSessionTableRow[] = $cell;
  81. }
  82. $cells = new DataObjectSet( $newRow );
  83. $table[] = $cells->customise( array( 'Cells' => $cells ) );
  84. $sessionTable[] = $newSessionTableRow;
  85. }
  86. }
  87. fclose( $tempFile );
  88. $this->table = new DataObjectSet( $table );
  89. Session::set("ImportFile.{$_REQUEST['ID']}", $sessionTable);
  90. return $this->renderWith( 'Newsletter_RecipientImportField_Table' );
  91. }
  92. /**
  93. * Determines what type each column is
  94. */
  95. function parseTableHeader( $columns ) {
  96. $columnSource = array_combine(
  97. array_keys( self::$column_types ),
  98. array_keys( self::$column_types )
  99. );
  100. $columnSource = array_merge( array( 'Unknown' => 'Unknown' ), $columnSource );
  101. $colCount = 0;
  102. foreach( $columns as $cell ) {
  103. $columnType = $this->getColumnType( $this->parseCSVCell( $cell )->Value() );
  104. $this->tableColumns[] = new DropdownField( 'ImportFileColumns[' . (string)( $colCount++ ) . ']', '', $columnSource, $columnType );
  105. }
  106. }
  107. function parseCSVCell( $cell ) {
  108. return new RecipientImportField_Cell( $cell );
  109. }
  110. function getColumnType( $cell ) {
  111. $cell = strtolower( $cell );
  112. $escapedValue = preg_replace( '/[^a-z]/', '', $cell );
  113. foreach( self::$column_types as $type => $aliases ) {
  114. if( in_array( $escapedValue, $aliases ) )
  115. return $type;
  116. }
  117. return 'Unknown';
  118. }
  119. function setController( $controller ) {
  120. $this->controller = $controller;
  121. }
  122. /**
  123. * Set of table column headers
  124. */
  125. function ColumnHeaders() {
  126. return new DataObjectSet( $this->tableColumns );
  127. }
  128. function Rows() {
  129. return $this->table;
  130. }
  131. function FileName() {
  132. return $this->clientFileName;
  133. }
  134. function TypeID() {
  135. return $this->typeID;
  136. }
  137. }
  138. /**
  139. * Single cell of the recipient import field
  140. *
  141. * @package newsletter
  142. */
  143. class RecipientImportField_Cell extends ViewableData {
  144. protected $value;
  145. function __construct( $value ) {
  146. $this->value = $value;
  147. parent::__construct();
  148. }
  149. function Value() {
  150. return $this->value;
  151. }
  152. }
  153. /**
  154. * Upload form that appears in the iframe
  155. *
  156. * @package newsletter
  157. */
  158. class RecipientImportField_UploadForm extends Form {
  159. function import( $data, $form ) {
  160. $id = $data['ID'];
  161. $mailType = DataObject::get_one("NewsletterType", "ID = $id");
  162. if($mailType->GroupID)
  163. $group = DataObject::get_one("Group", "ID = $mailType->GroupID");
  164. $recipientField = new RecipientImportField("ImportFile","Import from file", $group );
  165. $recipientField->setTypeID( $id );
  166. // if the file is not valid, then return an error
  167. if( empty( $_FILES ) || empty( $_FILES['ImportFile'] ) || $_FILES['ImportFile']['size'] == 0 )
  168. return $this->customise( array( 'ID' => $id, "UploadForm" => $this->controller->UploadForm( $id ), 'ErrorMessage' => 'Please choose a CSV file to import' ) )->renderWith('Newsletter_RecipientImportField');
  169. elseif( !$this->isValidCSV( $_FILES['ImportFile'] ) ) {
  170. /*if( file_exists( $_FILES['ImportFile']['tmp_name'] ) ) unlink( $_FILES['ImportFile']['tmp_name'] );
  171. unset( $_FILES['ImportFile'] );*/
  172. return $this->customise( array( 'ID' => $id, "UploadForm" => $this->controller->UploadForm( $id ), 'ErrorMessage' => 'The selected file was not a CSV file' ) )->renderWith('Newsletter_RecipientImportField');
  173. } else
  174. return $recipientField->displaytable();
  175. }
  176. function isValidCSV( $file ) {
  177. return preg_match( '/.*\.csv$/i', $file['name'] ) > 0;
  178. }
  179. function confirm( $data, $form ) {
  180. $id = $data['ID'];
  181. $mailType = DataObject::get_one("NewsletterType", "ID = $id");
  182. if($mailType->GroupID)
  183. $group = DataObject::get_one("Group", "ID = $mailType->GroupID");
  184. if(isset($data['Set'])){
  185. $setFields = $data['Set'];
  186. }else{
  187. $setFields = null;
  188. }
  189. return $this->importMembers( $id, $group, $data['ImportFileColumns'], $setFields );
  190. }
  191. function cancel( $data, $form ) {
  192. $newForm = $this->controller->UploadForm( $data['ID'] );
  193. return $newForm->forTemplate();
  194. }
  195. function action_import( $data, $form ) {
  196. return $this->import( $data, $form );
  197. }
  198. function action_confirm( $data, $form ) {
  199. return $this->confirm( $data, $form );
  200. }
  201. function action_cancel( $data, $form ) {
  202. return $this->cancel( $data, $form );
  203. }
  204. /**
  205. * Import members from the uploaded file
  206. */
  207. protected function importMembers( $id, $group, $cols, $setFields, $primaryColType = 'Email' ) {
  208. $startTime = time();
  209. $importData = Session::get("ImportFile.{$id}");
  210. $validColumns = array_keys( RecipientImportField::$column_types );
  211. $columnMap = array_flip( $cols );
  212. // Debug::show($columnMap);
  213. // locate the primary column's index
  214. $primaryColumn = /*array_search( $primaryColType, $validColumns );*/ $columnMap[$primaryColType];
  215. // changed fields
  216. $changedFields = array();
  217. // intersect the list of valid columns with the column map to find the columns we need
  218. $importColumns = array_intersect( $validColumns, $cols );
  219. // statistics
  220. $newMembers = 0;
  221. $changedMembers = 0;
  222. $skippedMembers = 0;
  223. // the class that the imported members will become
  224. $newMemberClass = Object::getCustomClass( 'Member' );
  225. // for each row, add a new member or overwrite an existing member
  226. foreach( $importData as $newMemberRow ) {
  227. // skip rows with an empty value for the primary column
  228. if( empty( $newMemberRow[$primaryColumn] ) ) {
  229. $skippedMembers++;
  230. continue;
  231. }
  232. // remember to check if the user has unsubscribed
  233. $trackChanges = true;
  234. // TODO: Write DataObject::update
  235. $member = $this->findMember( $newMemberRow[$primaryColumn] );
  236. if( !$member ) {
  237. $newMembers++;
  238. $trackChanges = false;
  239. $member = Object::create("Member");
  240. } else {
  241. // skip this member if the are unsubscribed
  242. if( $member->Unsubscribed ) {
  243. $skippedMembers++;
  244. continue;
  245. }
  246. if( $member->class != $newMemberClass )
  247. $member->setClassName( $newMemberClass );
  248. $changedMembers++;
  249. }
  250. // add each of the valid columns
  251. foreach( $importColumns as $datum ) {
  252. // perform any required conversions
  253. $newValue = trim( $newMemberRow[$columnMap[$datum]] );
  254. $oldValue = trim( $member->$datum );
  255. // Debug::message( "$datum@{$columnMap[$datum]}" );
  256. // track the modifications to the member data
  257. if( $trackChanges && $newValue != $oldValue && $datum != $primaryColumn ) {
  258. $changedFields[] = array(
  259. $newMemberRow[$primaryColumn],
  260. "$datum:\n$oldValue",
  261. "$datum:\n$newValue"
  262. );
  263. $numChangedFields++;
  264. }
  265. $member->$datum = $newValue;
  266. }
  267. // set any set fields
  268. if( $setFields )
  269. foreach( $setFields as $fieldName => $fieldValue )
  270. $member->$fieldName = $fieldValue;
  271. // add member to group
  272. $member->write();
  273. $member->Groups()->add( $group->ID );
  274. }
  275. $numChangedFields = count( $changedFields );
  276. $this->notifyChanges( $changedFields );
  277. // TODO Refresh window
  278. $customData = array(
  279. 'ID' => $id,
  280. "UploadForm" => $this->controller->UploadForm( $id ),
  281. 'ImportMessage' => 'Imported new members',
  282. 'NewMembers' => (string)$newMembers,
  283. 'ChangedMembers' => (string)$changedMembers,
  284. 'ChangedFields' => (string)$numChangedFields,
  285. 'SkippedRecords' => (string)$skippedMembers,
  286. 'Time' => time() - $startTime
  287. );
  288. return $this->customise( $customData )->renderWith('Newsletter_RecipientImportField');
  289. }
  290. function findMember( $email ) {
  291. $email = addslashes( $email );
  292. if(defined('DB::USE_ANSI_SQL')) {
  293. return DataObject::get_one( 'Member', "\"Email\"='$email'" );
  294. } else {
  295. return DataObject::get_one( 'Member', "`Email`='$email'" );
  296. }
  297. }
  298. function notifyChanges( $changes ) {
  299. $email = new Email( Email::getAdminEmail(), Email::getAdminEmail(), 'Changed Fields' );
  300. $body = "";
  301. foreach( $changes as $change ) {
  302. $body .= "-------------------------------\n";
  303. $body .= implode( ' ', $change ) . "\n";
  304. }
  305. $email->setBody( $body );
  306. $email->send();
  307. }
  308. }