diff --git a/src/Application/Controller/XMLRPC/Handler.php b/src/Application/Controller/XMLRPC/Handler.php
index 1d7f1b3..de18bab 100644
--- a/src/Application/Controller/XMLRPC/Handler.php
+++ b/src/Application/Controller/XMLRPC/Handler.php
@@ -437,7 +437,7 @@ public function setCommenceTicketState($ticket_id) {
throw new Exception(__FUNCTION__.': ticket is not in initial state!',1304);
}
- $commenceState = ProjectTicketState::getCommenceState($ticket['project_id'], $ticket['ticket_type']);
+ $commenceState = ProjectTicketState::getCommenceState($ticket['project_id'], $ticket['ticket_type'], $ticket_id);
if(!$commenceState) {
throw new Exception(__FUNCTION__.': ticket has no commence state!',1305);
}
diff --git a/src/Application/Migrations/04_projects.sql b/src/Application/Migrations/04_projects.sql
index f6a6799..86a5ab9 100644
--- a/src/Application/Migrations/04_projects.sql
+++ b/src/Application/Migrations/04_projects.sql
@@ -50,6 +50,7 @@ CREATE TABLE tbl_project_ticket_state
ticket_type enum_ticket_type NOT NULL,
ticket_state enum_ticket_state NOT NULL,
service_executable boolean NOT NULL DEFAULT false,
+ skip_on_dependent boolean NOT NULL DEFAULT false,
CONSTRAINT tbl_project_ticket_state_pk PRIMARY KEY (project_id, ticket_type, ticket_state),
CONSTRAINT tbl_project_ticket_state_project_fk FOREIGN KEY (project_id)
REFERENCES tbl_project (id) MATCH SIMPLE
diff --git a/src/Application/Migrations/06_tickets.sql b/src/Application/Migrations/06_tickets.sql
index f8e83e2..e7d2ce8 100644
--- a/src/Application/Migrations/06_tickets.sql
+++ b/src/Application/Migrations/06_tickets.sql
@@ -40,7 +40,7 @@ $BODY$
DECLARE
next_state record;
BEGIN
- next_state := ticket_state_next(NEW.project_id, NEW.ticket_type, NEW.ticket_state);
+ next_state := ticket_state_next(NEW.project_id, NEW.ticket_type, NEW.ticket_state, NEW.id);
NEW.ticket_state_next := next_state.ticket_state;
NEW.service_executable := next_state.service_executable;
@@ -76,7 +76,7 @@ $BODY$
(progress, ticket_state_next, service_executable)
= (tp, (n).ticket_state, (n).service_executable)
FROM (
- SELECT id, ticket_state_next(t2.project_id, t2.ticket_type, t2.ticket_state) AS n, ticket_progress(t2.id) as tp
+ SELECT id, ticket_state_next(t2.project_id, t2.ticket_type, t2.ticket_state, t2.id) AS n, ticket_progress(t2.id) as tp
FROM tbl_ticket t2
WHERE t2.project_id = param_project_id AND param_project_id IS NOT NULL
) AS x
diff --git a/src/Application/Migrations/15_function_ticket_state.sql b/src/Application/Migrations/15_function_ticket_state.sql
index 01c6d67..55ad941 100644
--- a/src/Application/Migrations/15_function_ticket_state.sql
+++ b/src/Application/Migrations/15_function_ticket_state.sql
@@ -2,7 +2,7 @@ BEGIN;
SET ROLE TO postgres;
-CREATE OR REPLACE FUNCTION ticket_state_next(param_project_id bigint, param_ticket_type enum_ticket_type, param_ticket_state enum_ticket_state)
+CREATE OR REPLACE FUNCTION ticket_state_next(param_ticket_id bigint)
RETURNS TABLE(ticket_state enum_ticket_state, service_executable boolean) AS
$$
DECLARE
@@ -11,17 +11,61 @@ BEGIN
SELECT
pts.ticket_state, pts.service_executable
FROM
- tbl_ticket_state ts1
+ tbl_ticket t
JOIN
- tbl_project_ticket_state pts ON pts.ticket_type = ts1.ticket_type AND pts.ticket_state = ts1.ticket_state
+ tbl_ticket_state ts_this ON ts_this.ticket_type = t.ticket_type AND ts_this.ticket_state = t.ticket_state
JOIN
- tbl_ticket_state ts2 ON ts1.ticket_type = ts2.ticket_type AND ts1.sort > ts2.sort
+ tbl_project_ticket_state pts ON pts.project_id = t.project_id AND pts.ticket_type = t.ticket_type
+ JOIN
+ tbl_ticket_state ts_other ON ts_other.ticket_type = pts.ticket_type AND ts_other.ticket_state = pts.ticket_state
WHERE
- pts.project_id = param_project_id AND
- ts2.ticket_type = param_ticket_type AND
- ts2.ticket_state = param_ticket_state
- ORDER BY
- ts1.sort ASC
+ t.id = param_ticket_id AND
+ ts_other.sort > ts_this.sort AND
+ (pts.skip_on_dependent = FALSE OR
+ ( /* is master encoding ticket */
+ SELECT ep.depends_on
+ FROM tbl_ticket t
+ JOIN tbl_encoding_profile_version epv ON epv.id = t.encoding_profile_version_id
+ JOIN tbl_encoding_profile ep ON ep.id = epv.encoding_profile_id
+ WHERE t.id = param_ticket_id
+ ) IS NULL )
+ ORDER BY ts_other.sort ASC
+ LIMIT 1;
+ IF NOT FOUND THEN
+ RETURN QUERY SELECT NULL::enum_ticket_state, false;
+ END IF;
+END
+$$
+LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION ticket_state_previous(param_ticket_id bigint)
+ RETURNS TABLE(ticket_state enum_ticket_state, service_executable boolean) AS
+ $$
+DECLARE
+BEGIN
+ RETURN QUERY
+ SELECT
+ pts.ticket_state, pts.service_executable
+ FROM
+ tbl_ticket t
+ JOIN
+ tbl_ticket_state ts_this ON ts_this.ticket_type = t.ticket_type AND ts_this.ticket_state = t.ticket_state
+ JOIN
+ tbl_project_ticket_state pts ON pts.project_id = t.project_id AND pts.ticket_type = t.ticket_type
+ JOIN
+ tbl_ticket_state ts_other ON ts_other.ticket_type = pts.ticket_type AND ts_other.ticket_state = pts.ticket_state
+ WHERE
+ t.id = param_ticket_id AND
+ ts_other.sort < ts_this.sort AND
+ (pts.skip_on_dependent = FALSE OR
+ ( /* is master encoding ticket */
+ SELECT ep.depends_on
+ FROM tbl_ticket t
+ JOIN tbl_encoding_profile_version epv ON epv.id = t.encoding_profile_version_id
+ JOIN tbl_encoding_profile ep ON ep.id = epv.encoding_profile_id
+ WHERE t.id = param_ticket_id
+ ) IS NULL )
+ ORDER BY ts_other.sort DESC
LIMIT 1;
IF NOT FOUND THEN
RETURN QUERY SELECT NULL::enum_ticket_state, false;
@@ -30,7 +74,7 @@ END
$$
LANGUAGE plpgsql;
-CREATE OR REPLACE FUNCTION ticket_state_previous(param_project_id bigint, param_ticket_type enum_ticket_type, param_ticket_state enum_ticket_state)
+CREATE OR REPLACE FUNCTION ticket_state_previous(param_project_id bigint, param_ticket_type enum_ticket_type, param_ticket_state enum_ticket_state, param_ticket_id bigint default NULL)
RETURNS TABLE(ticket_state enum_ticket_state, service_executable boolean) AS
$$
DECLARE
@@ -47,7 +91,16 @@ BEGIN
WHERE
pts.project_id = param_project_id AND
ts2.ticket_type = param_ticket_type AND
- ts2.ticket_state = param_ticket_state
+ ts2.ticket_state = param_ticket_state AND
+ (pts.skip_on_dependent = false OR
+ ( /* is master encoding ticket */
+ SELECT ep.depends_on
+ FROM tbl_ticket t
+ JOIN tbl_encoding_profile_version epv ON epv.id = t.encoding_profile_version_id
+ JOIN tbl_encoding_profile ep ON ep.id = epv.encoding_profile_id
+ WHERE t.id = param_ticket_id
+ ) IS NULL
+ )
ORDER BY
ts1.sort DESC
LIMIT 1;
@@ -96,7 +149,7 @@ END
$$
LANGUAGE plpgsql;
-CREATE OR REPLACE FUNCTION ticket_state_commence(param_project_id bigint, param_ticket_type enum_ticket_type)
+CREATE OR REPLACE FUNCTION ticket_state_commence(param_project_id bigint, param_ticket_type enum_ticket_type, param_ticket_id bigint)
RETURNS enum_ticket_state AS
$$
DECLARE
@@ -111,7 +164,7 @@ BEGIN
ret := (SELECT ticket_state_initial(param_project_id, param_ticket_type));
WHILE ret IS NOT NULL LOOP
- SELECT * INTO next_state FROM ticket_state_next(param_project_id, param_ticket_type, ret);
+ SELECT * INTO next_state FROM ticket_state_next(param_ticket_id);
IF NOT FOUND THEN
ret := NULL;
EXIT;
@@ -129,4 +182,4 @@ END
$$
LANGUAGE plpgsql;
-COMMIT;
\ No newline at end of file
+COMMIT;
diff --git a/src/Application/Migrations/__tmp_upgrade_tables-2017-08-21-subencoding-skips.sql b/src/Application/Migrations/__tmp_upgrade_tables-2017-08-21-subencoding-skips.sql
new file mode 100644
index 0000000..3852198
--- /dev/null
+++ b/src/Application/Migrations/__tmp_upgrade_tables-2017-08-21-subencoding-skips.sql
@@ -0,0 +1,152 @@
+BEGIN;
+
+SET ROLE TO postgres;
+
+DROP FUNCTION ticket_state_next(bigint, enum_ticket_type, enum_ticket_state);
+CREATE OR REPLACE FUNCTION ticket_state_next(param_project_id bigint, param_ticket_type enum_ticket_type, param_ticket_state enum_ticket_state, param_ticket_id bigint default NULL)
+ RETURNS TABLE(ticket_state enum_ticket_state, service_executable boolean) AS
+$$
+DECLARE
+BEGIN
+ RETURN QUERY
+ SELECT
+ pts.ticket_state, pts.service_executable
+ FROM
+ tbl_ticket_state ts1
+ JOIN
+ tbl_project_ticket_state pts ON pts.ticket_type = ts1.ticket_type AND pts.ticket_state = ts1.ticket_state
+ JOIN
+ tbl_ticket_state ts2 ON ts1.ticket_type = ts2.ticket_type AND ts1.sort > ts2.sort
+ WHERE
+ pts.project_id = param_project_id AND
+ ts2.ticket_type = param_ticket_type AND
+ ts2.ticket_state = param_ticket_state AND
+ (pts.skip_on_dependent = false OR
+ ( /* is master encoding ticket */
+ SELECT ep.depends_on
+ FROM tbl_ticket t
+ JOIN tbl_encoding_profile_version epv ON epv.id = t.encoding_profile_version_id
+ JOIN tbl_encoding_profile ep ON ep.id = epv.encoding_profile_id
+ WHERE t.id = param_ticket_id
+ ) IS NULL
+ )
+ ORDER BY
+ ts1.sort ASC
+ LIMIT 1;
+ IF NOT FOUND THEN
+ RETURN QUERY SELECT NULL::enum_ticket_state, false;
+ END IF;
+END
+$$
+LANGUAGE plpgsql;
+
+DROP FUNCTION ticket_state_previous(bigint, enum_ticket_type, enum_ticket_state);
+CREATE OR REPLACE FUNCTION ticket_state_previous(param_project_id bigint, param_ticket_type enum_ticket_type, param_ticket_state enum_ticket_state, param_ticket_id bigint default NULL)
+ RETURNS TABLE(ticket_state enum_ticket_state, service_executable boolean) AS
+$$
+DECLARE
+BEGIN
+ RETURN QUERY
+ SELECT
+ pts.ticket_state, pts.service_executable
+ FROM
+ tbl_ticket_state ts1
+ JOIN
+ tbl_project_ticket_state pts ON pts.ticket_type = ts1.ticket_type AND pts.ticket_state = ts1.ticket_state
+ JOIN
+ tbl_ticket_state ts2 ON ts1.ticket_type = ts2.ticket_type AND ts1.sort < ts2.sort
+ WHERE
+ pts.project_id = param_project_id AND
+ ts2.ticket_type = param_ticket_type AND
+ ts2.ticket_state = param_ticket_state AND
+ (pts.skip_on_dependent = false OR
+ ( /* is master encoding ticket */
+ SELECT ep.depends_on
+ FROM tbl_ticket t
+ JOIN tbl_encoding_profile_version epv ON epv.id = t.encoding_profile_version_id
+ JOIN tbl_encoding_profile ep ON ep.id = epv.encoding_profile_id
+ WHERE t.id = param_ticket_id
+ ) IS NULL
+ )
+ ORDER BY
+ ts1.sort DESC
+ LIMIT 1;
+ IF NOT FOUND THEN
+ RETURN QUERY SELECT NULL::enum_ticket_state, false;
+ END IF;
+END
+$$
+LANGUAGE plpgsql;
+
+DROP FUNCTION ticket_state_commence(bigint, enum_ticket_type);
+CREATE OR REPLACE FUNCTION ticket_state_commence(param_project_id bigint, param_ticket_type enum_ticket_type, param_ticket_id bigint)
+ RETURNS enum_ticket_state AS
+$$
+DECLARE
+ ret enum_ticket_state;
+ next_state record;
+BEGIN
+ -- special case: meta ticket, since it has no serviceable states
+ IF param_ticket_type = 'meta' THEN
+ RETURN 'staged'::enum_ticket_state;
+ END IF;
+
+ ret := (SELECT ticket_state_initial(param_project_id, param_ticket_type));
+
+ WHILE ret IS NOT NULL LOOP
+ SELECT * INTO next_state FROM ticket_state_next(param_project_id, param_ticket_type, ret, param_ticket_id);
+ IF NOT FOUND THEN
+ ret := NULL;
+ EXIT;
+ END IF;
+
+ -- exit, if serviceable state is found
+ EXIT WHEN next_state.service_executable IS TRUE;
+
+ -- otherwise set current state as possible commence state
+ ret := next_state.ticket_state;
+ END LOOP;
+
+ RETURN ret;
+END
+$$
+LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION update_ticket_next_state()
+ RETURNS trigger AS
+$BODY$
+DECLARE
+ next_state record;
+BEGIN
+ next_state := ticket_state_next(NEW.project_id, NEW.ticket_type, NEW.ticket_state, NEW.id);
+
+ NEW.ticket_state_next := next_state.ticket_state;
+ NEW.service_executable := next_state.service_executable;
+
+ RETURN NEW;
+END
+$BODY$
+LANGUAGE plpgsql VOLATILE;
+
+CREATE OR REPLACE FUNCTION update_all_tickets_progress_and_next_state(param_project_id bigint)
+ RETURNS VOID AS
+$BODY$
+BEGIN
+
+ UPDATE tbl_ticket t SET
+ (progress, ticket_state_next, service_executable)
+ = (tp, (n).ticket_state, (n).service_executable)
+ FROM (
+ SELECT id, ticket_state_next(t2.project_id, t2.ticket_type, t2.ticket_state, t2.id) AS n, ticket_progress(t2.id) as tp
+ FROM tbl_ticket t2
+ WHERE t2.project_id = param_project_id AND param_project_id IS NOT NULL
+ ) AS x
+ WHERE t.id = x.id;
+
+END;
+$BODY$
+LANGUAGE plpgsql VOLATILE;
+
+ALTER TABLE tbl_project_ticket_state ADD COLUMN skip_on_dependent BOOLEAN NOT NULL DEFAULT FALSE;
+
+COMMIT;
\ No newline at end of file
diff --git a/src/Application/Model/ProjectTicketState.php b/src/Application/Model/ProjectTicketState.php
index 89226f9..8868f9a 100644
--- a/src/Application/Model/ProjectTicketState.php
+++ b/src/Application/Model/ProjectTicketState.php
@@ -21,30 +21,30 @@ class ProjectTicketState extends Model {
];
// TODO: use Ticket::queryNextState / queryPreviousState?
- public static function getNextState($project, $type, $state) {
+ public static function getNextState($project, $type, $state, $ticket) {
$handle = Database::$Instance->query(
- 'SELECT * FROM ticket_state_next(?, ?, ?)',
- [$project, $type, $state]
+ 'SELECT * FROM ticket_state_next(?, ?, ?, ?)',
+ [$project, $type, $state, $ticket]
);
$row = $handle->fetch();
return ($row === false)? null : $row;
}
- public static function getPreviousState($project, $type, $state) {
+ public static function getPreviousState($project, $type, $state, $ticket) {
$handle = Database::$Instance->query(
- 'SELECT * FROM ticket_state_previous(?, ?, ?)',
- [$project, $type, $state]
+ 'SELECT * FROM ticket_state_previous(?, ?, ?, ?)',
+ [$project, $type, $state, $ticket]
);
$row = $handle->fetch();
return ($row === false)? null : $row;
}
- public static function getCommenceState($project, $type) {
+ public static function getCommenceState($project, $type, $ticket) {
$handle = Database::$Instance->query(
- 'SELECT ticket_state_commence(?, ?)',
- [$project, $type]
+ 'SELECT ticket_state_commence(?, ?, ?)',
+ [$project, $type, $ticket]
);
return $handle->fetch()['ticket_state_commence'];
diff --git a/src/Application/Model/Ticket.php b/src/Application/Model/Ticket.php
index 3cbefe4..744bef3 100644
--- a/src/Application/Model/Ticket.php
+++ b/src/Application/Model/Ticket.php
@@ -855,12 +855,13 @@ public function queryPreviousState($state = null) {
return (new Database_Query(''))
->select('ticket_state')
->from(
- 'ticket_state_previous(?, ?, ?)',
+ 'ticket_state_previous(?, ?, ?, ?)',
'previous_state',
[
$this['project_id'],
$this['ticket_type'],
- ($state === null)? $this['ticket_state'] : $state
+ ($state === null)? $this['ticket_state'] : $state,
+ $this['id']
]
);
}
@@ -869,16 +870,17 @@ public function queryNextState($state = null) {
return (new Database_Query(''))
->select('ticket_state')
->from(
- 'ticket_state_next(?, ?, ?)',
+ 'ticket_state_next(?, ?, ?, ?)',
'next_state',
[
$this['project_id'],
$this['ticket_type'],
- ($state === null)? $this['ticket_state'] : $state
+ ($state === null)? $this['ticket_state'] : $state,
+ $this['id']
]
);
}
-
+
public function findNextForAction($state) {
$next = null;
diff --git a/src/Application/Model/TicketState.php b/src/Application/Model/TicketState.php
index 8d195cb..86cc92b 100644
--- a/src/Application/Model/TicketState.php
+++ b/src/Application/Model/TicketState.php
@@ -9,7 +9,9 @@ class TicketState extends Model {
public $hasOne = [
'ProjectTicketState' => [
'foreign_key' => ['ticket_type', 'ticket_state'],
- 'select' => '(ticket_state IS NOT NULL) AS project_enabled, service_executable AS project_service_executable'
+ 'select' => '(ticket_state IS NOT NULL) AS project_enabled,' .
+ 'service_executable AS project_service_executable,' .
+ 'skip_on_dependent AS project_skip_on_dependent'
]
];
@@ -31,6 +33,11 @@ public static function getStateByAction($action) {
return self::$_actions[$action];
}
+
+ public static function isSkippable($state) {
+ return ($state === 'postencoded' ||
+ $state === 'checking');
+ }
public function defaultScope(Model_Resource $resource) {
$resource->orderBy('ticket_type, sort');
diff --git a/src/Application/View/projects/settings/states.html.php b/src/Application/View/projects/settings/states.html.php
index 00d0352..41496e8 100644
--- a/src/Application/View/projects/settings/states.html.php
+++ b/src/Application/View/projects/settings/states.html.php
@@ -1,7 +1,9 @@
title('States | '); ?>
= $this->render('projects/settings/_header'); ?>
-
= $f = $stateForm(['disabled' => $project['read_only']]); ?>
@@ -17,55 +19,84 @@
$encodingStates, (!empty($project))? $project['dependent_ticket_trigger_state'] : null) ?>
- $state): ?>
-
+ $state):
+ if ($type != $state['ticket_type']):
$type = $state['ticket_type'];
-
- if ($typeRows > 1):
- $typeRows = 0; ?>
-
-
-
-
-
-
-
-
- | Type |
- State |
- Service |
-
-
-
-
-
- | = h(mb_ucfirst($type)); ?> |
-
-
- |
-
- =
- $f->checkbox(
- 'States[' . $index . '][ticket_state]',
- $state['ticket_state'],
- $state['project_enabled'],
- ['value' => $state['ticket_state']] +
+ if ($type !== null):
+ if (!$firstType):
+ // close previous table ?>
+ |
+
+
+
+
+
+
+
+ | Type |
+ State |
+
+ title="=$title_service?>">Service
+
+ >
+
+ |
+
+ title="=$title_skip?>">Master only
+
+ >
+
+ |
+
+
+
+
+ | = h(mb_ucfirst($type)); ?> |
+
+
+ |
+
+
+ = $f->checkbox(
+ 'States[' . $index . '][ticket_state]',
+ $state['ticket_state'],
+ $state['project_enabled'],
+ ['value' => $state['ticket_state']] +
(($state['project_enabled'])?
['data-association-destroy' => 'States[' . $index . '][_destroy]'] :
[]),
- false
- ) .
- $f->hidden('States[' . $index . '][ticket_type]', $state['ticket_type']);
- ?> |
- checkbox('States[' . $index . '][service_executable]', null, $state['project_service_executable'], [], false);
- } ?> |
-
- register('States[' . $index . '][_destroy]'); ?>
-
+ false
+ ) .
+ $f->hidden('States[' . $index . '][ticket_type]', $state['ticket_type']);
+ ?>
+
+
+ checkbox('States[' . $index . '][service_executable]', null, $state['project_service_executable'], ['title' => $title_service], false);
+ } ?>
+ |
+
+ checkbox('States[' . $index . '][skip_on_dependent]', null, $state['project_skip_on_dependent'], ['title' => $title_skip], false);
+ } ?>
+ |
+
+ register('States[' . $index . '][_destroy]');
+ endforeach; ?>
-
+
+
diff --git a/src/Public/css/main.css b/src/Public/css/main.css
index 26dcf55..7205a02 100644
--- a/src/Public/css/main.css
+++ b/src/Public/css/main.css
@@ -2542,6 +2542,15 @@ ul.project-settings-header li.current a.hooks:before {
margin-left: 0.5em;
}
+.stages-container .column-50:nth-child(even) {
+ float:right;
+}
+.stages-container .column-50:nth-child(odd) {
+
+ padding-right: 15px;
+ padding-left: 0;
+}
+
.progress-width[data-progress="0"] { width: 0%; }
.progress-width[data-progress="1"] { width: 1%; }
.progress-width[data-progress="2"] { width: 2%; }