How do you create an angular material theme using ONLY hex values? - angular-material

Absolutely every single demo I've seen for creating an angular material theme involves the use of a pre-defined color palette (e.g. $mat-blue). I want to create a theme starting solely from HEX values. How can I do this? (Surely I'm not expected to start with my HEX values and somehow work backward to find the corresponding pallete in material's extremely limited predefined universe?!)
Here's an example of the HEX values I want to use:
https://material.io/resources/color/#!/?view.left=0&view.right=1&primary.color=2C3E50&secondary.color=95a5a6&primary.text.color=ffffff&secondary.text.color=000000
Edit: for clarification, I know how to create custom themes using pre-defined colors supplied by angular-material (e.g. basing $primary off of $mat-blue), but what I want to be able to base $primary off of e.g. #2C3E50.

Here's the workflow I settled on to get as close as possible to what I wanted to achieve.
First of all, make sure you know your scss fundamentals when attempting theming. If you don't know what these items and their syntax are (i.e. how they translate to css):
map and get-map
mixin
function
... then you need to first read the linked documentation. (They're not complicated and won't take long.) At the end of the day, angular themes are just scss maps of maps, so building them and accessing their contents just comes down to some scss know-how.
In angular material, you basically start with three choices of color to build your theme around: primary, accent, and warn. Suppose you choose the three colors #375a7f, #444444, and #eb0000 respectively. Go to this site and, for each color, name the palette (e.g. darkprimarymap) and input the HEX color into the menu like so:
... then click on View Code, select Angular 2, then copy and paste the code you see from something like this:
... into your theming file. An "out-of-the-box" theming file looks like this:
$app-dark-primary: mat-palette($mat-grey, 700, 300, 900);
$app-dark-accent: mat-palette($mat-blue-grey, 400);
$app-dark-warn: mat-palette($mat-red, 500);
$app-dark-theme: mat-dark-theme($app-dark-primary, $app-dark-accent, $app-dark-warn);
... where mat-palette is a function that accepts nested maps of the form we just copied. So paste the code we copied to create alternative palette variables like this:
#import '~#angular/material/theming';
// Create nested scss map
$dark-primary-map: (
50: #e7ebf0,
100: #c3ced9,
200: #9badbf,
300: #738ca5,
400: #557392,
500: #375a7f, // original primary color you built this map around
600: #315277,
700: #2a486c,
800: #233f62,
900: #162e4f,
A100: #8bb8ff,
A200: #5898ff,
A400: #2579ff,
A700: #0c69ff,
contrast: (
50: #000000,
100: #000000,
200: #000000,
300: #000000,
400: #ffffff,
500: #ffffff,
600: #ffffff,
700: #ffffff,
800: #ffffff,
900: #ffffff,
A100: #000000,
A200: #000000,
A400: #ffffff,
A700: #ffffff
)
);
$dark-accent-map: (
50: #e9e9e9,
100: #c7c7c7,
200: #a2a2a2,
300: #7c7c7c,
400: #606060,
500: #444444, // original accent color you built this map around
600: #3e3e3e,
700: #353535,
800: #2d2d2d,
900: #1f1f1f,
A100: #f07a7a,
A200: #eb4c4c,
A400: #ff0505,
A700: #eb0000,
contrast: (
50: #000000,
100: #000000,
200: #000000,
300: #ffffff,
400: #ffffff,
500: #ffffff,
600: #ffffff,
700: #ffffff,
800: #ffffff,
900: #ffffff,
A100: #000000,
A200: #ffffff,
A400: #ffffff,
A700: #ffffff
)
);
$dark-warn-map: (
50: #fde0e0,
100: #f9b3b3,
200: #f58080,
300: #f14d4d,
400: #ee2626,
500: #eb0000, // original warn color you built this map around
600: #e90000,
700: #e50000,
800: #e20000,
900: #dd0000,
A100: #ffffff,
A200: #ffd1d1,
A400: #ff9e9e,
A700: #ff8585,
contrast: (
50: #000000,
100: #000000,
200: #000000,
300: #ffffff,
400: #ffffff,
500: #ffffff,
600: #ffffff,
700: #ffffff,
800: #ffffff,
900: #ffffff,
A100: #000000,
A200: #000000,
A400: #000000,
A700: #000000
)
);
// Convert maps into 'palettes'
$dark-primary-palette: mat-palette($dark-primary-map);
$dark-accent-palette: mat-palette($dark-accent-map);
$dark-warn-palette: mat-palette($dark-warn-map);
// Create 'theme' from palettes
$app-dark-theme: mat-dark-theme($dark-primary-palette, $dark-accent-palette, $dark-warn-palette);
You can of course manually create/edit these maps (which might be smart since the linked site makes simplistic extrapolations from your single-color input). If this code is in a file called dark-theme.scss, you then incorporate that theme into your angular app with this sort of syntax in your main/global styles.scss file:
#import '~#angular/material/theming';
#include mat-core();
#import 'themes/dark-theme.scss'; // imports $app-dark-theme
.dark-theme {
#include angular-material-theme($app-dark-theme);
#include custom-components-theme($app-dark-theme);
}
Now, when you color a material component with the color="primary" directive on this theme, it will get colored as #375a7f, and so on.
To pull other colors out of these palettes for use in your app in customized components, you use this sort of syntax in a app.component.scss-theme.scss file:
#import '~#angular/material/theming';
// Define scss mixin that takes a $theme map and injects its content
// into css styles; we'll inject our $app-dark-theme here later
#mixin app-component-theme($theme) {
// Get the primary, secondary and warn palettes
// you created back from the active theme using the map-get() function
$primary: map-get($theme, primary);
$accent: map-get($theme, accent);
$warn: map-get($theme, warn);
// Extract colors from those palettes using the mat-color() function
// E.g. use the 'darker' input to get the color keyed by '700'
$color1: mat-color($primary, darker);
// E.g. use 'A100' to get the color keyed by 'A100' (A = Additional I think)
$color2: mat-color($primary, A100);
// E.g. use 'A100-contrast' to get the color keyed by 'A100' within the contrast sub-map
$color3: mat-color($primary, A100-contrast);
// Then use these color-value variables as per normal scss. E.g.
mat-sidenav-container {
background: $color1;
mat-toolbar {
background-color: $color2;
mat-list{
background-color: $color3;
}
}
}
}
... then incorporate all such custom-component themes into your main/global styles.scss file like so:
#import 'component.scss-theme';
// Define custom component themes
#mixin custom-components-theme($theme) {
#include app-component-theme($theme);
}

Related

Microsoft Graph API Beta ToDo List API Fails with 401

EDIT: I have successfully used the endpoint with my business Office365 account. I would like to use this in conjunction with my personal account since it has the shared data I am looking to access. Is there some fundamental aspect that prevents me from accessing this information through these APIs?
I have tried to perform the "my To Do task lists" https://graph.microsoft.com/beta/me/todo/lists with the Microsoft Graph Explorer Tool at https://developer.microsoft.com/en-us/graph/graph-explorer
I have set the Tasks.ReadWrite consent for the token, and it lists it "Consented" in the "Modify Permissions" tab. However, calling the API just returns a 401 error with a very unhelpful message:
{
"error": {
"code": "UnknownError",
"message": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n<head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\"/>\r\n<title>401 - Unauthorized: Access is denied due to invalid credentials.</title>\r\n<style type=\"text/css\">\r\n<!--\r\nbody{margin:0;font-size:.7em;font-family:Verdana, Arial, Helvetica, sans-serif;background:#EEEEEE;}\r\nfieldset{padding:0 15px 10px 15px;} \r\nh1{font-size:2.4em;margin:0;color:#FFF;}\r\nh2{font-size:1.7em;margin:0;color:#CC0000;} \r\nh3{font-size:1.2em;margin:10px 0 0 0;color:#000000;} \r\n#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:\"trebuchet MS\", Verdana, sans-serif;color:#FFF;\r\nbackground-color:#555555;}\r\n#content{margin:0 0 0 2%;position:relative;}\r\n.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}\r\n-->\r\n</style>\r\n</head>\r\n<body>\r\n<div id=\"header\"><h1>Server Error</h1></div>\r\n<div id=\"content\">\r\n <div class=\"content-container\"><fieldset>\r\n <h2>401 - Unauthorized: Access is denied due to invalid credentials.</h2>\r\n <h3>You do not have permission to view this directory or page using the credentials that you supplied.</h3>\r\n </fieldset></div>\r\n</div>\r\n</body>\r\n</html>\r\n",
"innerError": {
"date": "2020-10-08T17:48:38",
"request-id": "7989b12e-4276-4600-968a-daa59677ebfe",
"client-request-id": "019b2755-0eb0-40f2-7ada-6df8d36857ff"
}
}
}
Yes – I know something is wrong but I thought I was doing everything properly. The account I am using is a personal account.
Other requests, such as https://graph.microsoft.com/beta/me work just fine. I have not found in the documentation any obvious things I am missing, so I thought I'd try to hear if someone has had a similar issue.

Why is the Microsoft Graph Planner APIs suddenly failing for external guest users in a Team?

From approximately March 23 2020 Microsoft Graph Planner apis like https://graph.microsoft.com/v1.0/planner/tasks/TC41-x8azkKX-ibO17qtJpYAHGAB starting throwing 401 for external guest users with the following error message:
{
"error": {
"code": "UnknownError",
"message": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n<head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\"/>\r\n<title>401 - Unauthorized: Access is denied due to invalid credentials.</title>\r\n<style type=\"text/css\">\r\n<!--\r\nbody{margin:0;font-size:.7em;font-family:Verdana, Arial, Helvetica, sans-serif;background:#EEEEEE;}\r\nfieldset{padding:0 15px 10px 15px;} \r\nh1{font-size:2.4em;margin:0;color:#FFF;}\r\nh2{font-size:1.7em;margin:0;color:#CC0000;} \r\nh3{font-size:1.2em;margin:10px 0 0 0;color:#000000;} \r\n#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:\"trebuchet MS\", Verdana, sans-serif;color:#FFF;\r\nbackground-color:#555555;}\r\n#content{margin:0 0 0 2%;position:relative;}\r\n.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}\r\n-->\r\n</style>\r\n</head>\r\n<body>\r\n<div id=\"header\"><h1>Server Error</h1></div>\r\n<div id=\"content\">\r\n <div class=\"content-container\"><fieldset>\r\n <h2>401 - Unauthorized: Access is denied due to invalid credentials.</h2>\r\n <h3>You do not have permission to view this directory or page using the credentials that you supplied.</h3>\r\n </fieldset></div>\r\n</div>\r\n</body>\r\n</html>\r\n",
"innerError": {
"request-id": "33a1c8d0-60b3-46ab-807a-da5a8d776151",
"date": "2020-04-13T07:51:57"
}
}
}
The Microsoft Graph call has Group.ReadWrite permissions.
The same user can access the task fine through Microsoft Planner with this url: https://tasks.office.com/meetingdecisions.com/Home/Task/TC41-x8azkKX-ibO17qtJpYAHGAB
Why did this suddenly start happening?

FileNotFoundError in temporary job location during Cloud Dataflow Shuffle operation

While running a batch job in Google Cloud Dataflow, I am experiencing an error during a particular step that uses Dataflow's service-based Shuffle feature. The error claims that a specific file is no longer present in the temporary job location I specified for this pipeline.
Here is the most relevant piece of the full stacktrace:
An exception was raised when trying to execute the workitem 2931621256965625980 : Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/apache_beam/io/gcp/gcsio.py", line 490, in __init__
metadata = self._get_object_metadata(self._get_request)
File "/usr/local/lib/python3.7/site-packages/apache_beam/utils/retry.py", line 206, in wrapper
return fun(*args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/apache_beam/io/gcp/gcsio.py", line 513, in _get_object_metadata
return self._client.objects.Get(get_request)
File "/usr/local/lib/python3.7/site-packages/apache_beam/io/gcp/internal/clients/storage/storage_v1_client.py", line 1100, in Get
download=download)
File "/usr/local/lib/python3.7/site-packages/apitools/base/py/base_api.py", line 731, in _RunMethod
return self.ProcessHttpResponse(method_config, http_response, request)
File "/usr/local/lib/python3.7/site-packages/apitools/base/py/base_api.py", line 737, in ProcessHttpResponse
self.__ProcessHttpResponse(method_config, http_response, request))
File "/usr/local/lib/python3.7/site-packages/apitools/base/py/base_api.py", line 604, in __ProcessHttpResponse
http_response, method_config=method_config, request=request)
apitools.base.py.exceptions.HttpNotFoundError: HttpError accessing <https://www.googleapis.com/storage/v1/b/<CLOUD STORAGE PATH FOR TEMPORARY JOB FILES>%2F<DATAFLOW JOB NAME>.1571774420.011973%2Ftmp-626a66561e20e8b6-00000-of-00003.avro?alt=json>: response: <{'x-guploader-uploadid': 'AEnB2UrVuWRWrrcneEjgvuGSwYR82tBqDdVa727Ylo8tVW6ucnPdeNbE2A8DXf7mDYqKKP42NdJapXZLR1UbCjvJ8n7w2SOVTMGFsrcbywKD1K9yxMWez7k', 'content-type': 'application/json; charset=UTF-8', 'date': 'Tue, 22 Oct 2019 20:43:59 GMT', 'vary': 'Origin, X-Origin', 'cache-control': 'no-cache, no-store, max-age=0, must-revalidate', 'expires': 'Mon, 01 Jan 1990 00:00:00 GMT', 'pragma': 'no-cache', 'content-length': '473', 'server': 'UploadServer', 'status': '404'}>, content <{
"error": {
"code": 404,
"message": "No such object: <CLOUD STORAGE PATH FOR TEMPORARY JOB FILES>/<DATAFLOW JOB NAME>.1571774420.011973/tmp-626a66561e20e8b6-00000-of-00003.avro",
"errors": [
{
"message": "No such object: <CLOUD STORAGE PATH FOR TEMPORARY JOB FILES>/<DATAFLOW JOB NAME>.1571774420.011973/tmp-626a66561e20e8b6-00000-of-00003.avro",
"domain": "global",
"reason": "notFound"
}
]
}
}
Any advice on how to resolve this error would be appreciated. It seems to me that Dataflow created a temporary file during the course of the shuffle. That file must have been deleted shortly afterward, but sometime later Dataflow is attempting to access that file and causing this error.
After a lot of trial and error, I never really found an answer to this problem. The root cause is Dataflow's Shuffle service--it seems that if a particular shuffle step is very very expensive, these types of intermittent connection issues eventually cause the job to error out.
I ultimately solved this problem by re-working the data set to cut the amount of shuffling required by about half. The Shuffle service now runs reliably for me.
Cloud Dataflow shuffle is still an experimental feature--I am hoping that this kind of instability disappears as it matures.
You will need to add stagingLocation or gcpTempLocation to resolve this error.
You can check here [1] for further details.
1 - https://cloud.google.com/dataflow/docs/guides/specifying-exec-params#configuring-pipelineoptions-for-execution-on-the-cloud-dataflow-service

Microsoft Graph findMeetingTimes - 401 UnknownError

I have registered an application in Azure Active Directory to call findMeetingTimes from Microsoft Graph. I am able to successfully authorize, get a token, and make other Graph API calls, but receive a 401 - Unauthorized response with an error code of "UnknownError" for the endpoint https://graph.microsoft.com/v1.0/me/findMeetingTimes.
I am using a Microsoft work account for these activities, and I am able to login with said account and view the calendar. I have tried other calendar-related API calls including /me/calendar/getSchedule and /me/events and received successful responses. I am mostly testing with Postman, but have attempted to use the Graph Explorer and gotten the same response.
I have also checked that the token I am using contains the correct scope of both Calendars.Read.Shared and Calendars.ReadWrite.Shared. I originally tried with just the scope of Calendars.Read.Shared, but to no avail.
This is the response I receive when requesting a token:
{
"token_type": "Bearer",
"scope": "Calendars.Read.Shared Calendars.ReadWrite.Shared User.Read profile openid email",
"expires_in": 3600,
"ext_expires_in": 3600,
"access_token": "foo",
"refresh_token": "bar"
}
The request I make (with an empty body, as all body parameters are optional):
POST /v1.0/me/findMeetingTimes HTTP/1.1
Host: graph.microsoft.com
Content-Type: application/json
Authorization: Bearer foo
The 401 response I receive:
{
"error": {
"code": "UnknownError",
"message": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n<head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\"/>\r\n<title>401 - Unauthorized: Access is denied due to invalid credentials.</title>\r\n<style type=\"text/css\">\r\n<!--\r\nbody{margin:0;font-size:.7em;font-family:Verdana, Arial, Helvetica, sans-serif;background:#EEEEEE;}\r\nfieldset{padding:0 15px 10px 15px;} \r\nh1{font-size:2.4em;margin:0;color:#FFF;}\r\nh2{font-size:1.7em;margin:0;color:#CC0000;} \r\nh3{font-size:1.2em;margin:10px 0 0 0;color:#000000;} \r\n#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:\"trebuchet MS\", Verdana, sans-serif;color:#FFF;\r\nbackground-color:#555555;}\r\n#content{margin:0 0 0 2%;position:relative;}\r\n.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}\r\n-->\r\n</style>\r\n</head>\r\n<body>\r\n<div id=\"header\"><h1>Server Error</h1></div>\r\n<div id=\"content\">\r\n <div class=\"content-container\"><fieldset>\r\n <h2>401 - Unauthorized: Access is denied due to invalid credentials.</h2>\r\n <h3>You do not have permission to view this directory or page using the credentials that you supplied.</h3>\r\n </fieldset></div>\r\n</div>\r\n</body>\r\n</html>\r\n",
"innerError": {
"request-id": "foo",
"date": "2019-06-21T13:52:00"
}
}
}
Is there any other configuration that I can change to get around this issue?
We've managed to track down the issue: the O365 tenant I was attempting to make the API call on was a Government tenant and did not have the "findMeetingTimes" function enabled/installed within the tenant.
The workaround for the time being for us is to use the Outlook REST API, which has a similar function: https://learn.microsoft.com/en-us/previous-versions/office/office-365-api/api/version-2.0/calendar-rest-operations#FindMeetingTimes

Getting Task list from Microsoft Graph with app-only scopes

Is it possible to retrieve task lists using the Microsoft Graph API (http://graph.microsoft.io/docs/api-reference/beta/api/task_list) in a service app?
I can successfully request a token that has all of the following scopes:
Calendars.Read
Calendars.ReadWrite
Contacts.Read
Directory.AccessAsUser.All
Directory.Read
List item
Directory.Read.All
Directory.ReadWrite.All
email
Files.Read
Files.Read.Selected
Group.Read.All
Group.ReadWrite.All
Mail.Read
Mail.Send
Notes.Create
Notes.Read
Notes.Read.All
Notes.ReadWrite.All
offline_access
openid
People.Read
profile
Sites.Read.All
Tasks.ReadWrite
User.Read
User.Read.All
User.ReadBasic.All
User.ReadWrite
User.ReadWrite.All
user_impersonation
I am able to get lists of users, groups, applications and other entities, but my requests to the tasks or plans endpoints return the following error:
{
"error": {
"code": "UnknownError",
"message": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n<head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\"/>\r\n<title>401 - Unauthorized: Access is denied due to invalid credentials.</title>\r\n<style type=\"text/css\">\r\n<!--\r\nbody{margin:0;font-size:.7em;font-family:Verdana, Arial, Helvetica, sans-serif;background:#EEEEEE;}\r\nfieldset{padding:0 15px 10px 15px;} \r\nh1{font-size:2.4em;margin:0;color:#FFF;}\r\nh2{font-size:1.7em;margin:0;color:#CC0000;} \r\nh3{font-size:1.2em;margin:10px 0 0 0;color:#000000;} \r\n#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:\"trebuchet MS\", Verdana, sans-serif;color:#FFF;\r\nbackground-color:#555555;}\r\n#content{margin:0 0 0 2%;position:relative;}\r\n.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}\r\n-->\r\n</style>\r\n</head>\r\n<body>\r\n<div id=\"header\"><h1>Server Error</h1></div>\r\n<div id=\"content\">\r\n <div class=\"content-container\"><fieldset>\r\n <h2>401 - Unauthorized: Access is denied due to invalid credentials.</h2>\r\n <h3>You do not have permission to view this directory or page using the credentials that you supplied.</h3>\r\n </fieldset></div>\r\n</div>\r\n</body>\r\n</html>\r\n",
"innerError": {
"request-id": "d0d7d970-ff88-4af9-93d3-8b8d4acfabf0",
"date": "2016-01-28T12:46:31"
}
}
}
My expectation is that Group.ReadWrite.All is inherited as app-only from Directory.ReadWrite.All, so together with Task.ReadWrite I should be able to see tasks and plans.
Is there another required permission or another setting that we’ve missed?
Retrieval of tasks/plans is currently not supported in service-only (app-only) context. Please use user delegation context (app+user) instead.

Resources