diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -356,6 +356,7 @@ 'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18', 'rsrc/js/application/config/behavior-reorder-fields.js' => '14a827de', 'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => '6709c934', + 'rsrc/js/application/conpherence/behavior-drag-and-drop-photo.js' => 'cf86d16a', 'rsrc/js/application/conpherence/behavior-durable-column.js' => '657c2b50', 'rsrc/js/application/conpherence/behavior-menu.js' => '804b0773', 'rsrc/js/application/conpherence/behavior-pontificate.js' => '21ba5861', @@ -561,6 +562,7 @@ 'javelin-behavior-audit-preview' => 'd835b03a', 'javelin-behavior-choose-control' => '6153c708', 'javelin-behavior-config-reorder-fields' => '14a827de', + 'javelin-behavior-conpherence-drag-and-drop-photo' => 'cf86d16a', 'javelin-behavior-conpherence-menu' => '804b0773', 'javelin-behavior-conpherence-pontificate' => '21ba5861', 'javelin-behavior-conpherence-widget-pane' => '93568464', @@ -1789,6 +1791,12 @@ 'javelin-stratcom', 'phabricator-phtize', ), + 'cf86d16a' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-workflow', + 'phabricator-drag-and-drop-file-upload', + ), 'd19198c8' => array( 'javelin-install', 'javelin-dom', diff --git a/resources/sql/autopatches/20150501.conpherencepics.sql b/resources/sql/autopatches/20150501.conpherencepics.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20150501.conpherencepics.sql @@ -0,0 +1,5 @@ +ALTER TABLE {$NAMESPACE}_conpherence.conpherence_thread + ADD imagePHIDs LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL AFTER title; + +UPDATE {$NAMESPACE}_conpherence.conpherence_thread + SET imagePHIDS = '[]'; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -230,8 +230,10 @@ 'ConpherenceDurableColumnView' => 'applications/conpherence/view/ConpherenceDurableColumnView.php', 'ConpherenceEditor' => 'applications/conpherence/editor/ConpherenceEditor.php', 'ConpherenceFileWidgetView' => 'applications/conpherence/view/ConpherenceFileWidgetView.php', + 'ConpherenceFormDragAndDropUploadControl' => 'applications/conpherence/view/ConpherenceFormDragAndDropUploadControl.php', 'ConpherenceFulltextQuery' => 'applications/conpherence/query/ConpherenceFulltextQuery.php', 'ConpherenceHovercardEventListener' => 'applications/conpherence/events/ConpherenceHovercardEventListener.php', + 'ConpherenceImageData' => 'applications/conpherence/constants/ConpherenceImageData.php', 'ConpherenceIndex' => 'applications/conpherence/storage/ConpherenceIndex.php', 'ConpherenceLayoutView' => 'applications/conpherence/view/ConpherenceLayoutView.php', 'ConpherenceListController' => 'applications/conpherence/controller/ConpherenceListController.php', @@ -244,6 +246,7 @@ 'ConpherenceParticipantQuery' => 'applications/conpherence/query/ConpherenceParticipantQuery.php', 'ConpherenceParticipationStatus' => 'applications/conpherence/constants/ConpherenceParticipationStatus.php', 'ConpherencePeopleWidgetView' => 'applications/conpherence/view/ConpherencePeopleWidgetView.php', + 'ConpherencePicCropControl' => 'applications/conpherence/view/ConpherencePicCropControl.php', 'ConpherenceQueryThreadConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceQueryThreadConduitAPIMethod.php', 'ConpherenceQueryTransactionConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceQueryTransactionConduitAPIMethod.php', 'ConpherenceReplyHandler' => 'applications/conpherence/mail/ConpherenceReplyHandler.php', @@ -3467,8 +3470,10 @@ 'ConpherenceDurableColumnView' => 'AphrontTagView', 'ConpherenceEditor' => 'PhabricatorApplicationTransactionEditor', 'ConpherenceFileWidgetView' => 'ConpherenceWidgetView', + 'ConpherenceFormDragAndDropUploadControl' => 'AphrontFormControl', 'ConpherenceFulltextQuery' => 'PhabricatorOffsetPagedQuery', 'ConpherenceHovercardEventListener' => 'PhabricatorEventListener', + 'ConpherenceImageData' => 'ConpherenceConstants', 'ConpherenceIndex' => 'ConpherenceDAO', 'ConpherenceLayoutView' => 'AphrontView', 'ConpherenceListController' => 'ConpherenceController', @@ -3481,6 +3486,7 @@ 'ConpherenceParticipantQuery' => 'PhabricatorOffsetPagedQuery', 'ConpherenceParticipationStatus' => 'ConpherenceConstants', 'ConpherencePeopleWidgetView' => 'ConpherenceWidgetView', + 'ConpherencePicCropControl' => 'AphrontFormControl', 'ConpherenceQueryThreadConduitAPIMethod' => 'ConpherenceConduitAPIMethod', 'ConpherenceQueryTransactionConduitAPIMethod' => 'ConpherenceConduitAPIMethod', 'ConpherenceReplyHandler' => 'PhabricatorMailReplyHandler', diff --git a/src/applications/conpherence/constants/ConpherenceImageData.php b/src/applications/conpherence/constants/ConpherenceImageData.php new file mode 100644 --- /dev/null +++ b/src/applications/conpherence/constants/ConpherenceImageData.php @@ -0,0 +1,11 @@ +setViewer($user) ->withPHIDs($conpherence_phids) + ->needCropPics(true) ->needParticipantCache(true) ->execute(); $latest_conpherences = mpull($latest_conpherences, null, 'getPHID'); @@ -30,6 +31,7 @@ $conpherence = id(new ConpherenceThreadQuery()) ->setViewer($user) ->withIDs(array($request->getInt('id'))) + ->needCropPics(true) ->needTransactions(true) ->setTransactionLimit(ConpherenceThreadQuery::TRANSACTION_LIMIT) ->executeOne(); @@ -39,6 +41,7 @@ $conpherence = id(new ConpherenceThreadQuery()) ->setViewer($user) ->withPHIDs(array($participant->getConpherencePHID())) + ->needCropPics(true) ->needTransactions(true) ->setTransactionLimit(ConpherenceThreadQuery::TRANSACTION_LIMIT) ->executeOne(); diff --git a/src/applications/conpherence/controller/ConpherenceListController.php b/src/applications/conpherence/controller/ConpherenceListController.php --- a/src/applications/conpherence/controller/ConpherenceListController.php +++ b/src/applications/conpherence/controller/ConpherenceListController.php @@ -138,6 +138,7 @@ $conpherences = id(new ConpherenceThreadQuery()) ->setViewer($user) ->withPHIDs($conpherence_phids) + ->needCropPics(true) ->needParticipantCache(true) ->execute(); diff --git a/src/applications/conpherence/controller/ConpherenceNotificationPanelController.php b/src/applications/conpherence/controller/ConpherenceNotificationPanelController.php --- a/src/applications/conpherence/controller/ConpherenceNotificationPanelController.php +++ b/src/applications/conpherence/controller/ConpherenceNotificationPanelController.php @@ -17,6 +17,7 @@ $conpherences = id(new ConpherenceThreadQuery()) ->setViewer($user) ->withPHIDs(array_keys($participant_data)) + ->needCropPics(true) ->needTransactions(true) ->setTransactionLimit(3 * 5) ->needParticipantCache(true) diff --git a/src/applications/conpherence/controller/ConpherenceUpdateController.php b/src/applications/conpherence/controller/ConpherenceUpdateController.php --- a/src/applications/conpherence/controller/ConpherenceUpdateController.php +++ b/src/applications/conpherence/controller/ConpherenceUpdateController.php @@ -37,6 +37,8 @@ ->setViewer($user) ->withIDs(array($conpherence_id)) ->needFilePHIDs(true) + ->needOrigPics(true) + ->needCropPics(true) ->needParticipants($need_participants) ->requireCapabilities($needed_capabilities) ->executeOne(); @@ -132,11 +134,56 @@ ->setContent($result); break; case ConpherenceUpdateActions::METADATA: - // all metadata updates are continue requests + $top = $request->getInt('image_y'); + $left = $request->getInt('image_x'); + $file_id = $request->getInt('file_id'); + $title = $request->getStr('title'); + if ($file_id) { + $orig_file = id(new PhabricatorFileQuery()) + ->setViewer($user) + ->withIDs(array($file_id)) + ->executeOne(); + $xactions[] = id(new ConpherenceTransaction()) + ->setTransactionType(ConpherenceTransactionType::TYPE_PICTURE) + ->setNewValue($orig_file); + $okay = $orig_file->isTransformableImage(); + if ($okay) { + $xformer = new PhabricatorImageTransformer(); + $crop_file = $xformer->executeConpherenceTransform( + $orig_file, + 0, + 0, + ConpherenceImageData::CROP_WIDTH, + ConpherenceImageData::CROP_HEIGHT); + $xactions[] = id(new ConpherenceTransaction()) + ->setTransactionType( + ConpherenceTransactionType::TYPE_PICTURE_CROP) + ->setNewValue($crop_file->getPHID()); + } + $response_mode = 'redirect'; + } + + // all other metadata updates are continue requests if (!$request->isContinueRequest()) { break; } + if ($top !== null || $left !== null) { + $file = $conpherence->getImage(ConpherenceImageData::SIZE_ORIG); + $xformer = new PhabricatorImageTransformer(); + $xformed = $xformer->executeConpherenceTransform( + $file, + $top, + $left, + ConpherenceImageData::CROP_WIDTH, + ConpherenceImageData::CROP_HEIGHT); + $image_phid = $xformed->getPHID(); + + $xactions[] = id(new ConpherenceTransaction()) + ->setTransactionType( + ConpherenceTransactionType::TYPE_PICTURE_CROP) + ->setNewValue($image_phid); + } $title = $request->getStr('title'); $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(ConpherenceTransactionType::TYPE_TITLE) @@ -323,6 +370,35 @@ ->setName('title') ->setValue($conpherence->getTitle())); + $nopic = $this->getRequest()->getExists('nopic'); + $image = $conpherence->getImage(ConpherenceImageData::SIZE_ORIG); + if ($nopic) { + // do not render any pic related controls + } else if ($image) { + $crop_uri = $conpherence->loadImageURI(ConpherenceImageData::SIZE_CROP); + $form + ->appendChild( + id(new AphrontFormMarkupControl()) + ->setLabel(pht('Image')) + ->setValue(phutil_tag( + 'img', + array( + 'src' => $crop_uri, + )))) + ->appendChild( + id(new ConpherencePicCropControl()) + ->setLabel(pht('Crop Image')) + ->setValue($image)) + ->appendChild( + id(new ConpherenceFormDragAndDropUploadControl()) + ->setLabel(pht('Change Image'))); + } else { + $form + ->appendChild( + id(new ConpherenceFormDragAndDropUploadControl()) + ->setLabel(pht('Image'))); + } + if ($conpherence->getIsRoom()) { $title = pht('Update Room'); $policies = id(new PhabricatorPolicyQuery()) @@ -400,6 +476,7 @@ $conpherence = id(new ConpherenceThreadQuery()) ->setViewer($user) ->setAfterTransactionID($latest_transaction_id) + ->needCropPics(true) ->needParticipantCache($need_participant_cache) ->needWidgetData($need_widget_data) ->needTransactions($need_transactions) diff --git a/src/applications/conpherence/controller/ConpherenceViewController.php b/src/applications/conpherence/controller/ConpherenceViewController.php --- a/src/applications/conpherence/controller/ConpherenceViewController.php +++ b/src/applications/conpherence/controller/ConpherenceViewController.php @@ -15,6 +15,7 @@ $query = id(new ConpherenceThreadQuery()) ->setViewer($user) ->withIDs(array($conpherence_id)) + ->needCropPics(true) ->needParticipantCache(true) ->needTransactions(true) ->setTransactionLimit($this->getMainQueryLimit()); diff --git a/src/applications/conpherence/editor/ConpherenceEditor.php b/src/applications/conpherence/editor/ConpherenceEditor.php --- a/src/applications/conpherence/editor/ConpherenceEditor.php +++ b/src/applications/conpherence/editor/ConpherenceEditor.php @@ -120,6 +120,8 @@ $types[] = ConpherenceTransactionType::TYPE_TITLE; $types[] = ConpherenceTransactionType::TYPE_PARTICIPANTS; $types[] = ConpherenceTransactionType::TYPE_FILES; + $types[] = ConpherenceTransactionType::TYPE_PICTURE; + $types[] = ConpherenceTransactionType::TYPE_PICTURE_CROP; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; $types[] = PhabricatorTransactions::TYPE_JOIN_POLICY; @@ -134,6 +136,10 @@ switch ($xaction->getTransactionType()) { case ConpherenceTransactionType::TYPE_TITLE: return $object->getTitle(); + case ConpherenceTransactionType::TYPE_PICTURE: + return $object->getImagePHID(ConpherenceImageData::SIZE_ORIG); + case ConpherenceTransactionType::TYPE_PICTURE_CROP: + return $object->getImagePHID(ConpherenceImageData::SIZE_CROP); case ConpherenceTransactionType::TYPE_PARTICIPANTS: if ($this->getIsNewObject()) { return array(); @@ -150,7 +156,11 @@ switch ($xaction->getTransactionType()) { case ConpherenceTransactionType::TYPE_TITLE: + case ConpherenceTransactionType::TYPE_PICTURE_CROP: return $xaction->getNewValue(); + case ConpherenceTransactionType::TYPE_PICTURE: + $file = $xaction->getNewValue(); + return $file->getPHID(); case ConpherenceTransactionType::TYPE_PARTICIPANTS: case ConpherenceTransactionType::TYPE_FILES: return $this->getPHIDTransactionNewValue($xaction); @@ -243,6 +253,16 @@ case ConpherenceTransactionType::TYPE_TITLE: $object->setTitle($xaction->getNewValue()); break; + case ConpherenceTransactionType::TYPE_PICTURE: + $object->setImagePHID( + $xaction->getNewValue(), + ConpherenceImageData::SIZE_ORIG); + break; + case ConpherenceTransactionType::TYPE_PICTURE_CROP: + $object->setImagePHID( + $xaction->getNewValue(), + ConpherenceImageData::SIZE_CROP); + break; case ConpherenceTransactionType::TYPE_PARTICIPANTS: if (!$this->getIsNewObject()) { $old_map = array_fuse($xaction->getOldValue()); @@ -576,6 +596,20 @@ ); } + protected function extractFilePHIDsFromCustomTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case ConpherenceTransactionType::TYPE_PICTURE: + return array($xaction->getNewValue()->getPHID()); + case ConpherenceTransactionType::TYPE_PICTURE_CROP: + return array($xaction->getNewValue()); + } + + return parent::extractFilePHIDsFromCustomTransaction($object, $xaction); + } + protected function validateTransaction( PhabricatorLiskDAO $object, $type, @@ -608,6 +642,21 @@ $errors[] = $error; } break; + case ConpherenceTransactionType::TYPE_PICTURE: + foreach ($xactions as $xaction) { + $file = $xaction->getNewValue(); + if (!$file->isTransformableImage()) { + $detail = pht('This server only supports these image formats: %s.', + implode(', ', PhabricatorFile::getTransformableImageFormats())); + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Invalid'), + $detail, + last($xactions)); + $errors[] = $error; + } + } + break; case ConpherenceTransactionType::TYPE_PARTICIPANTS: foreach ($xactions as $xaction) { $new_phids = $this->getPHIDTransactionNewValue($xaction, array()); diff --git a/src/applications/conpherence/query/ConpherenceThreadQuery.php b/src/applications/conpherence/query/ConpherenceThreadQuery.php --- a/src/applications/conpherence/query/ConpherenceThreadQuery.php +++ b/src/applications/conpherence/query/ConpherenceThreadQuery.php @@ -11,6 +11,8 @@ private $isRoom; private $needParticipants; private $needWidgetData; + private $needCropPics; + private $needOrigPics; private $needTransactions; private $needParticipantCache; private $needFilePHIDs; @@ -39,6 +41,16 @@ return $this; } + public function needCropPics($need) { + $this->needCropPics = $need; + return $this; + } + + public function needOrigPics($need_widget_data) { + $this->needOrigPics = $need_widget_data; + return $this; + } + public function needTransactions($need_transactions) { $this->needTransactions = $need_transactions; return $this; @@ -122,6 +134,15 @@ if ($this->needWidgetData) { $this->loadWidgetData($conpherences); } + if ($this->needOrigPics || $this->needCropPics) { + $this->initImages($conpherences); + } + if ($this->needOrigPics) { + $this->loadOrigPics($conpherences); + } + if ($this->needCropPics) { + $this->loadCropPics($conpherences); + } } return $conpherences; @@ -395,6 +416,50 @@ return $this; } + private function loadOrigPics(array $conpherences) { + return $this->loadPics( + $conpherences, + ConpherenceImageData::SIZE_ORIG); + } + + private function loadCropPics(array $conpherences) { + return $this->loadPics( + $conpherences, + ConpherenceImageData::SIZE_CROP); + } + + private function initImages($conpherences) { + foreach ($conpherences as $conpherence) { + $conpherence->attachImages(array()); + } + } + + private function loadPics(array $conpherences, $size) { + $conpherence_pic_phids = array(); + foreach ($conpherences as $conpherence) { + $phid = $conpherence->getImagePHID($size); + if ($phid) { + $conpherence_pic_phids[$conpherence->getPHID()] = $phid; + } + } + + if (!$conpherence_pic_phids) { + return $this; + } + + $files = id(new PhabricatorFileQuery()) + ->setViewer($this->getViewer()) + ->withPHIDs($conpherence_pic_phids) + ->execute(); + $files = mpull($files, null, 'getPHID'); + + foreach ($conpherence_pic_phids as $conpherence_phid => $pic_phid) { + $conpherences[$conpherence_phid]->setImage($files[$pic_phid], $size); + } + + return $this; + } + public function getQueryApplicationClass() { return 'PhabricatorConpherenceApplication'; } diff --git a/src/applications/conpherence/storage/ConpherenceThread.php b/src/applications/conpherence/storage/ConpherenceThread.php --- a/src/applications/conpherence/storage/ConpherenceThread.php +++ b/src/applications/conpherence/storage/ConpherenceThread.php @@ -8,6 +8,7 @@ PhabricatorDestructibleInterface { protected $title; + protected $imagePHIDs = array(); protected $isRoom = 0; protected $messageCount; protected $recentParticipantPHIDs = array(); @@ -21,7 +22,7 @@ private $handles = self::ATTACHABLE; private $filePHIDs = self::ATTACHABLE; private $widgetData = self::ATTACHABLE; - private $images = array(); + private $images = self::ATTACHABLE; public static function initializeNewThread(PhabricatorUser $sender) { return id(new ConpherenceThread()) @@ -29,6 +30,7 @@ ->setTitle('') ->attachParticipants(array()) ->attachFilePHIDs(array()) + ->attachImages(array()) ->setViewPolicy(PhabricatorPolicies::POLICY_USER) ->setEditPolicy(PhabricatorPolicies::POLICY_USER) ->setJoinPolicy(PhabricatorPolicies::POLICY_USER); @@ -42,6 +44,7 @@ ->setTitle('') ->attachParticipants(array()) ->attachFilePHIDs(array()) + ->attachImages(array()) ->setViewPolicy(PhabricatorPolicies::POLICY_USER) ->setEditPolicy($creator->getPHID()) ->setJoinPolicy(PhabricatorPolicies::POLICY_USER); @@ -52,6 +55,7 @@ self::CONFIG_AUX_PHID => true, self::CONFIG_SERIALIZATION => array( 'recentParticipantPHIDs' => self::SERIALIZATION_JSON, + 'imagePHIDs' => self::SERIALIZATION_JSON, ), self::CONFIG_COLUMN_SCHEMA => array( 'title' => 'text255?', @@ -89,6 +93,34 @@ return 'Z'.$this->getID(); } + public function getImagePHID($size) { + $image_phids = $this->getImagePHIDs(); + return idx($image_phids, $size); + } + public function setImagePHID($phid, $size) { + $image_phids = $this->getImagePHIDs(); + $image_phids[$size] = $phid; + return $this->setImagePHIDs($image_phids); + } + + public function getImage($size) { + $images = $this->getImages(); + return idx($images, $size); + } + public function setImage(PhabricatorFile $file, $size) { + $files = $this->getImages(); + $files[$size] = $file; + return $this->attachImages($files); + } + public function attachImages(array $files) { + assert_instances_of($files, 'PhabricatorFile'); + $this->images = $files; + return $this; + } + private function getImages() { + return $this->assertAttached($this->images); + } + public function attachParticipants(array $participants) { assert_instances_of($participants, 'ConpherenceParticipant'); $this->participants = $participants; @@ -157,6 +189,16 @@ return $this->assertAttached($this->widgetData); } + public function loadImageURI($size) { + $file = $this->getImage($size); + + if ($file) { + return $file->getBestURI(); + } + + return PhabricatorUser::getDefaultProfileImageURI(); + } + public function getDisplayData(PhabricatorUser $user) { if ($this->hasAttachedTransactions()) { $transactions = $this->getTransactions(); @@ -201,7 +243,10 @@ } $img_src = null; - if ($lucky_handle) { + $size = ConpherenceImageData::SIZE_CROP; + if ($this->getImagePHID($size)) { + $img_src = $this->getImage($size)->getBestURI(); + } else if ($lucky_handle) { $img_src = $lucky_handle->getImageURI(); } diff --git a/src/applications/conpherence/storage/ConpherenceTransaction.php b/src/applications/conpherence/storage/ConpherenceTransaction.php --- a/src/applications/conpherence/storage/ConpherenceTransaction.php +++ b/src/applications/conpherence/storage/ConpherenceTransaction.php @@ -32,12 +32,11 @@ case ConpherenceTransactionType::TYPE_PARTICIPANTS: return ($old === null); case ConpherenceTransactionType::TYPE_TITLE: + case ConpherenceTransactionType::TYPE_PICTURE: case ConpherenceTransactionType::TYPE_DATE_MARKER: return false; case ConpherenceTransactionType::TYPE_FILES: return true; - // we used to have them so just always hide - case ConpherenceTransactionType::TYPE_PICTURE: case ConpherenceTransactionType::TYPE_PICTURE_CROP: return true; } @@ -56,6 +55,7 @@ case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_JOIN_POLICY: + case ConpherenceTransactionType::TYPE_PICTURE: if ($this->getObject()->getIsRoom()) { return $this->getRoomTitle(); } else { @@ -144,6 +144,11 @@ } return $title; break; + case ConpherenceTransactionType::TYPE_PICTURE: + return pht( + '%s updated the room image.', + $this->renderHandleLink($author_phid)); + break; case PhabricatorTransactions::TYPE_VIEW_POLICY: return pht( '%s changed the visibility of this room from "%s" to "%s".', @@ -195,6 +200,11 @@ } return $title; break; + case ConpherenceTransactionType::TYPE_PICTURE: + return pht( + '%s updated the room image.', + $this->renderHandleLink($author_phid)); + break; case PhabricatorTransactions::TYPE_VIEW_POLICY: return pht( '%s changed the visibility of this thread from "%s" to "%s".', @@ -228,6 +238,7 @@ $phids[] = $this->getAuthorPHID(); switch ($this->getTransactionType()) { case ConpherenceTransactionType::TYPE_TITLE: + case ConpherenceTransactionType::TYPE_PICTURE: case ConpherenceTransactionType::TYPE_FILES: case ConpherenceTransactionType::TYPE_DATE_MARKER: break; diff --git a/src/applications/conpherence/view/ConpherenceDurableColumnView.php b/src/applications/conpherence/view/ConpherenceDurableColumnView.php --- a/src/applications/conpherence/view/ConpherenceDurableColumnView.php +++ b/src/applications/conpherence/view/ConpherenceDurableColumnView.php @@ -412,7 +412,7 @@ array( 'name' => $rename_label, 'disabled' => !$can_edit, - 'href' => '/conpherence/update/'.$conpherence->getID().'/', + 'href' => '/conpherence/update/'.$conpherence->getID().'/?nopic', 'icon' => 'fa-pencil', 'key' => ConpherenceUpdateActions::METADATA, ), diff --git a/src/applications/conpherence/view/ConpherenceFormDragAndDropUploadControl.php b/src/applications/conpherence/view/ConpherenceFormDragAndDropUploadControl.php new file mode 100644 --- /dev/null +++ b/src/applications/conpherence/view/ConpherenceFormDragAndDropUploadControl.php @@ -0,0 +1,40 @@ +dropID = $drop_id; + return $this; + } + public function getDropID() { + return $this->dropID; + } + + protected function getCustomControlClass() { + return null; + } + + protected function renderInput() { + + $drop_id = celerity_generate_unique_node_id(); + Javelin::initBehavior('conpherence-drag-and-drop-photo', + array( + 'target' => $drop_id, + 'form_pane' => 'conpherence-form', + 'upload_uri' => '/file/dropupload/', + 'activated_class' => 'conpherence-dialogue-upload-photo', + )); + require_celerity_resource('conpherence-update-css'); + + return phutil_tag( + 'div', + array( + 'id' => $drop_id, + 'class' => 'conpherence-dialogue-drag-photo', + ), + pht('Drag and drop an image here to upload it.')); + } + +} diff --git a/src/applications/conpherence/view/ConpherencePicCropControl.php b/src/applications/conpherence/view/ConpherencePicCropControl.php new file mode 100644 --- /dev/null +++ b/src/applications/conpherence/view/ConpherencePicCropControl.php @@ -0,0 +1,78 @@ +getValue(); + + if ($file === null) { + return phutil_tag( + 'img', + array( + 'src' => PhabricatorUser::getDefaultProfileImageURI(), + ), + ''); + } + + $c_id = celerity_generate_unique_node_id(); + $metadata = $file->getMetadata(); + $scale = PhabricatorImageTransformer::getScaleForCrop( + $file, + $width, + $height); + + Javelin::initBehavior( + 'aphront-crop', + array( + 'cropBoxID' => $c_id, + 'width' => $width, + 'height' => $height, + 'scale' => $scale, + 'imageH' => $metadata[PhabricatorFile::METADATA_IMAGE_HEIGHT], + 'imageW' => $metadata[PhabricatorFile::METADATA_IMAGE_WIDTH], + )); + + return javelin_tag( + 'div', + array( + 'id' => $c_id, + 'sigil' => 'crop-box', + 'mustcapture' => true, + 'class' => 'crop-box', + ), + array( + javelin_tag( + 'img', + array( + 'src' => $file->getBestURI(), + 'class' => 'crop-image', + 'sigil' => 'crop-image', + ), + ''), + javelin_tag( + 'input', + array( + 'type' => 'hidden', + 'name' => 'image_x', + 'sigil' => 'crop-x', + ), + ''), + javelin_tag( + 'input', + array( + 'type' => 'hidden', + 'name' => 'image_y', + 'sigil' => 'crop-y', + ), + ''), + )); + } + +} diff --git a/src/applications/conpherence/view/ConpherenceTransactionView.php b/src/applications/conpherence/view/ConpherenceTransactionView.php --- a/src/applications/conpherence/view/ConpherenceTransactionView.php +++ b/src/applications/conpherence/view/ConpherenceTransactionView.php @@ -256,6 +256,8 @@ $content = $transaction->getTitle(); break; case ConpherenceTransactionType::TYPE_TITLE: + case ConpherenceTransactionType::TYPE_PICTURE: + case ConpherenceTransactionType::TYPE_PICTURE_CROP: case ConpherenceTransactionType::TYPE_PARTICIPANTS: case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: diff --git a/webroot/rsrc/js/application/conpherence/behavior-drag-and-drop-photo.js b/webroot/rsrc/js/application/conpherence/behavior-drag-and-drop-photo.js new file mode 100644 --- /dev/null +++ b/webroot/rsrc/js/application/conpherence/behavior-drag-and-drop-photo.js @@ -0,0 +1,38 @@ +/** + * @provides javelin-behavior-conpherence-drag-and-drop-photo + * @requires javelin-behavior + * javelin-dom + * javelin-workflow + * phabricator-drag-and-drop-file-upload + */ + +JX.behavior('conpherence-drag-and-drop-photo', function(config) { + + var target = JX.$(config.target); + var form_pane = JX.$(config.form_pane); + + function onupload(f) { + var data = { + 'file_id' : f.getID(), + 'action' : 'metadata' + }; + + var form = JX.DOM.find(form_pane, 'form'); + var workflow = JX.Workflow.newFromForm(form, data); + workflow.start(); + } + + if (JX.PhabricatorDragAndDropFileUpload.isSupported()) { + var drop = new JX.PhabricatorDragAndDropFileUpload(target) + .setURI(config.upload_uri); + drop.listen('didBeginDrag', function() { + JX.DOM.alterClass(target, config.activated_class, true); + }); + drop.listen('didEndDrag', function() { + JX.DOM.alterClass(target, config.activated_class, false); + }); + drop.listen('didUpload', onupload); + drop.start(); + } + +});