Page MenuHomePhabricator

Support Projects in Calendar
Closed, ResolvedPublic

Description

You should be able to add projects to a Calendar event, similar to how you can add them to Tasks, Revisions, etc.

  • Implement PhabricatorProjectInterface on CalendarEvent.
  • Implement the transaction and editing UI.
  • Implement search support in ApplicationSearch using EdgeLogic.

Event Timeline

epriestley assigned this task to lpriestley.
epriestley raised the priority of this task from to Normal.
epriestley updated the task description. (Show Details)
epriestley added a project: Calendar.
epriestley added a subscriber: epriestley.

It is already possible to invite and subscribe projects. Ain't that even better than just tagging?

Invite/Subscribe for Projects is for adding all people, like "Engineering" to an event. Tagging with a Project is different, as it's just stating this Event is defined by being a part of "Product" or whatever. Specifically, we intend to have a "Project Calendar" which will list everything happening by date for that Project (not people invited to said events).

Maybe a better way to think about it, at least as I understand it, is Invite/Subscribe sends it to individual people and their calendars, but just tagging will not.

In the middle of refactoring the search engine, started writing a custom date search field, but I'm not sure how to get/set it's value.

<?php

final class PhabricatorSearchDateControlField
  extends PhabricatorSearchField {

  private $value;
  private $default;

  public function setValue(AphrontFormDateControlValue $value) {
    $this->value = $value;
    return $this;
  }

  public function getValue() {
    return $this->value;
  }

  protected function getValueFromRequest(AphrontRequest $request, $key) {
    return $request->getStr($key);
  }

  protected function newControl() {
    return new AphrontFormDateControl();
  }
}

$this->value is always null, despite it showing up in the UI with "today" date. How should I get the AphrontFormDateControlValue from the DateControlField?

This might not be fully correct, but it should get you a little further:

<?php

final class PhabricatorSearchDateControlField
  extends PhabricatorSearchField {

  private $value;

  public function setValue(AphrontFormDateControlValue $value) {
    $this->value = $value;
    return $this;
  }

  public function getValue() {
    return $this->value;
  }

  protected function getValueExistsInRequest(AphrontRequest $request, $key) {
    // The control doesn't actually submit a value with the same name as the
    // key, so look for the "_d" value instead, which has the date part of the
    // control value.
    return $request->getExists($key.'_d');
  }

  protected function getValueFromRequest(AphrontRequest $request, $key) {
    $value = AphrontFormDateControlValue::newFromRequest($request, $key);
    return $value->getDictionary();
  }

  protected function newControl() {
    return new AphrontFormDateControl();
  }

  protected function didReadValueFromSavedQuery($value) {
    if (!$value) {
      return null;
    }

    return AphrontFormDateControlValue::newFromWild($this->getViewer(), $value);
  }

}

Major differences are:

  • Implement getValueExistsInRequest() -- we won't try to read the value if this function doesn't return true. By default, it just looks for a variable in the request with the same name as the SearchField, so if the SearchField is called startDate, it looks for startDate. But DateControls don't actually submit a value called startDate, only startDate_d (date) and startDate_t (time). Instead of startDate, look for startDate_d. This isn't a very obvious piece of logic but we need it in order to support "aliases" for controls.
  • Use AphrontFormDateControlValue::newFromRequest(...) in getValueFromRequest(), since we need to run the code to parse out the _d and _t components.
  • Return ->getDictionary() from getValueFromRequest() because that value ends up saved to the database so it needs to be a string/array/etc. This isn't very obvious from how SearchField is written right now, and should probably be made more obvious.
  • Parse ::newFromWild() in didReadValueFromSavedQuery(). This mostly undoes the getDictionary() call, but also accommodates legacy queries from very long ago which might just have an epoch.