Skip to content

Commit

Permalink
Tags field type
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonkelly committed Jul 12, 2013
1 parent 104c98c commit 0a9ff80
Show file tree
Hide file tree
Showing 16 changed files with 563 additions and 37 deletions.
57 changes: 57 additions & 0 deletions src/controllers/TagsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,61 @@ public function actionDeleteTagSet()
craft()->tags->deleteTagSetById($sectionId);
$this->returnJson(array('success' => true));
}

/**
* Searches for tags.
*/
public function actionSearchForTags()
{
$this->requirePostRequest();
$this->requireAjaxRequest();

$search = craft()->request->getPost('search');
$source = craft()->request->getPost('source');
$excludeIds = craft()->request->getPost('excludeIds');

$criteria = craft()->elements->getCriteria(ElementType::Tag, array(
'search' => 'name:'.$search.'*',
'source' => $source,
'id' => 'not '.implode(',', $excludeIds)
));

$tags = $criteria->find();

$return = array();
$exactMatches = array();
$tagNameLengths = array();
$exactMatch = false;

$normalizedSearch = StringHelper::normalizeKeywords($search);

foreach ($tags as $tag)
{
$return[] = array(
'id' => $tag->id,
'name' => $tag->name
);

$tagNameLengths[] = strlen($tag->name);

$normalizedName = StringHelper::normalizeKeywords($tag->name);

if ($normalizedName == $normalizedSearch)
{
$exactMatches[] = 1;
$exactMatch = true;
}
else
{
$exactMatches[] = 0;
}
}

array_multisort($exactMatches, SORT_DESC, $tagNameLengths, $return);

$this->returnJson(array(
'tags' => $return,
'exactMatch' => $exactMatch
));
}
}
24 changes: 21 additions & 3 deletions src/elementtypes/TagElementType.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function getSources()

$sources[$key] = array(
'label' => $tagSet->name,
'criteria' => array('tagset' => $tagSet->id)
'criteria' => array('tagsetId' => $tagSet->id)
);
}

Expand Down Expand Up @@ -69,8 +69,10 @@ public function defineTableAttributes($source = null)
public function defineCriteriaAttributes()
{
return array(
'name' => AttributeType::String,
'order' => array(AttributeType::String, 'default' => 'name desc'),
'name' => AttributeType::String,
'tagset' => AttributeType::Mixed,
'tagsetId' => AttributeType::Mixed,
'order' => array(AttributeType::String, 'default' => 'name desc'),
);
}

Expand All @@ -86,6 +88,22 @@ public function modifyElementsQuery(DbCommand $query, ElementCriteriaModel $crit
$query
->addSelect('tags.name')
->join('tags tags', 'tags.id = elements.id');

if ($criteria->name)
{
$query->andWhere(DbHelper::parseParam('tags.name', $criteria->name, $query->params));
}

if ($criteria->tagsetId)
{
$query->andWhere(DbHelper::parseParam('tags.setId', $criteria->tagsetId, $query->params));
}

if ($criteria->tagset)
{
$query->join('tagsets tagsets', 'tagsets.id = tags.setId');
$query->andWhere(DbHelper::parseParam('tagsets.handle', $criteria->tagset, $query->params));
}
}

/**
Expand Down
12 changes: 6 additions & 6 deletions src/fieldtypes/BaseElementFieldType.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ abstract class BaseElementFieldType extends BaseFieldType
*/
public function getName()
{
return $this->_getElementType()->getName();
return $this->getElementType()->getName();
}

/**
Expand Down Expand Up @@ -75,7 +75,7 @@ public function getSettingsHtml()
{
return craft()->templates->render('_components/fieldtypes/elementfieldsettings', array(
'allowMultipleSources' => $this->allowMultipleSources,
'sources' => $this->_getElementType()->getSources(),
'sources' => $this->getElementType()->getSources(),
'settings' => $this->getSettings(),
'type' => $this->getName()
));
Expand Down Expand Up @@ -146,7 +146,7 @@ public function getInputHtml($name, $elements)

return craft()->templates->render('_includes/forms/elementSelect', array(
'jsClass' => $this->inputJsClass,
'elementType' => new ElementTypeVariable($this->_getElementType()),
'elementType' => new ElementTypeVariable($this->getElementType()),
'id' => $id,
'name' => $name,
'elements' => $elements->all,
Expand Down Expand Up @@ -176,18 +176,18 @@ public function onAfterElementSave()
protected function getAddButtonLabel()
{
return Craft::t('Add {type}', array(
'type' => strtolower($this->_getElementType()->getClassHandle())
'type' => strtolower($this->getElementType()->getClassHandle())
));
}

/**
* Returns the element type.
*
* @access private
* @access protected
* @return BaseElementType
* @throws Exception
*/
private function _getElementType()
protected function getElementType()
{
$elementType = craft()->elements->getElementType($this->elementType);

Expand Down
101 changes: 97 additions & 4 deletions src/fieldtypes/TagsFieldType.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
*/
class TagsFieldType extends BaseElementFieldType
{
private $_tagSetId;

/**
* @access protected
* @var string $elementType The element type this field deals with.
Expand All @@ -19,13 +21,104 @@ class TagsFieldType extends BaseElementFieldType
protected $allowMultipleSources = false;

/**
* Returns the label for the "Add" button.
* Returns the field's input HTML.
*
* @access protected
* @param string $name
* @param mixed $elements
* @return string
*/
protected function getAddButtonLabel()
public function getInputHtml($name, $elements)
{
return Craft::t('Add a tag');
$id = rtrim(preg_replace('/[\[\]]+/', '-', $name), '-');

if (!($elements instanceof RelationFieldData))
{
$elements = new RelationFieldData();
}

return craft()->templates->render('_components/fieldtypes/Tags/input', array(
'elementType' => new ElementTypeVariable($this->getElementType()),
'id' => $id,
'name' => $name,
'elements' => $elements->all,
'source' => $this->getSettings()->source,
'limit' => $this->getSettings()->limit,
'elementId' => (!empty($this->element->id) ? $this->element->id : null),
));
}

/**
* Performs any additional actions after the element has been saved.
*/
public function onAfterElementSave()
{
$tagSetId = $this->_getTagSetId();

if ($tagSetId === false)
{
return;
}

$rawValue = $this->element->getRawContent($this->model->handle);
$tagIds = is_array($rawValue) ? array_filter($rawValue) : array();

foreach ($tagIds as $i => $tagId)
{
if (strncmp($tagId, 'new:', 4) == 0)
{
$name = substr($tagId, 4);

// Last-minute check
$criteria = craft()->elements->getCriteria(Elementtype::Tag, array(
'tagsetId' => $tagSetId,
'name' => $name
));

$ids = $criteria->ids();

if ($ids)
{
$tagIds[$i] = $ids[0];
}
else
{
$tag = new TagModel();
$tag->setId = $tagSetId;
$tag->name = $name;

if (craft()->tags->saveTag($tag))
{
$tagIds[$i] = $tag->id;
}
}
}
}

craft()->relations->saveRelations($this->model->id, $this->element->id, $tagIds);
}

/**
* Returns the tag set ID this field is associated with.
*
* @access private
* @return int|false
*/
private function _getTagSetId()
{
if (!isset($this->_tagSetId))
{
$source = $this->getSettings()->source;

if (strncmp($source, 'tagset:', 7) == 0)
{
$this->_tagSetId = (int) substr($source, 7);
}
else
{
$this->_tagSetId = false;
}
}

return $this->_tagSetId;
}
}
13 changes: 7 additions & 6 deletions src/migrations/m130715_000001_tags.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ public function safeUp()
MigrationHelper::makeElemental('tags', 'Tag');

// Make some tweaks on the tags table
$this->alterColumn('tags', 'name', array('column' => ColumnType::Varchar));
$this->alterColumn('tags', 'name', array('column' => ColumnType::Varchar, 'null' => false));
$this->dropColumn('tags', 'count');
$this->addColumnBefore('tags', 'setId', array('column' => ColumnType::Int), 'name');
$this->addColumnBefore('tags', 'setId', array('column' => ColumnType::Int, 'null' => false), 'name');
$this->dropIndex('tags', 'name', true);

// Place all current tags into the Default group
Expand Down Expand Up @@ -76,10 +76,11 @@ public function safeUp()
}

$this->insert('fields', array(
'groupId' => $groupId,
'name' => 'Tags',
'handle' => $handle,
'type' => 'Tags'
'groupId' => $groupId,
'name' => 'Tags',
'handle' => $handle,
'type' => 'Tags',
'settings' => JsonHelper::encode(array('source' => 'tagset:'.$tagSetId)),
));

$fieldId = craft()->db->getLastInsertID();
Expand Down
4 changes: 2 additions & 2 deletions src/models/TagModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ function __toString()
protected function defineAttributes()
{
return array_merge(parent::defineAttributes(), array(
'setId' => AttributeType::Number,
'name' => AttributeType::String,
'setId' => AttributeType::Number,
'name' => AttributeType::String,
));
}

Expand Down
2 changes: 1 addition & 1 deletion src/records/TagRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function getTableName()
protected function defineAttributes()
{
return array(
'name' => AttributeType::String,
'name' => array(AttributeType::String, 'required' => true),
);
}

Expand Down
11 changes: 11 additions & 0 deletions src/resources/css/craft.scss
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,17 @@ table.data.collapsed tbody td form { display: inline-block; }
.elementselect .element { z-index: 1; }
.elementselect .caboose { float: left; }

/* tag select fields */
.tagselect .add { position: relative; z-index: 1; display: inline-block; width: 12em; }
.tagselect .add .text { padding-right: 30px; }
.tagselect .add .spinner { position: absolute; top: 0; right: 5px; }

.tagmenu { margin-left: -1px !important; min-width: 12em;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}

/* editable tables */
table.editable tbody tr:not(:first-child) td { border-top: 1px solid rgba(0,0,0,0.07); }
table.editable thead tr th,
Expand Down
8 changes: 6 additions & 2 deletions src/resources/js/classes/BaseElementSelectInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Craft.BaseElementSelectInput = Garnish.Base.extend({

this.totalElements = this.$elements.length;

if (this.limit && this.totalElements == this.limit)
if (this.limit && this.totalElements >= this.limit)
{
this.$addElementBtn.addClass('disabled');
}
Expand Down Expand Up @@ -79,7 +79,11 @@ Craft.BaseElementSelectInput = Garnish.Base.extend({
}

this.totalElements--;
this.$addElementBtn.removeClass('disabled');

if (this.$addElementBtn)
{
this.$addElementBtn.removeClass('disabled');
}

$element.css('z-index', 0);

Expand Down
4 changes: 2 additions & 2 deletions src/resources/js/classes/FieldLayoutDesigner.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Craft.FieldLayoutDesigner = Garnish.Base.extend({
}

var $option = $(option),
$tab = $option.data('menu').$btn.parent().parent().parent(),
$tab = $option.data('menu').$trigger.parent().parent().parent(),
action = $option.data('action');

switch (action)
Expand All @@ -118,7 +118,7 @@ Craft.FieldLayoutDesigner = Garnish.Base.extend({
onFieldOptionSelect: function(option)
{
var $option = $(option),
$field = $option.data('menu').$btn.parent(),
$field = $option.data('menu').$trigger.parent(),
action = $option.data('action');

switch (action)
Expand Down
Loading

0 comments on commit 0a9ff80

Please sign in to comment.